quarta-feira, 20 de julho de 2016

Testando código legado utilizando Mock em C#


Olá pessoal, como tinha dito na postagem anterior, hoje irei falar um pouco como utilizar Mock em C# em testes unitários.

Irei utilizar um código legado em vb.net da empresa onde trabalho e converte-lo para C# e criar os devidos teste. Para testa-lo irei mockar os dados.

O exemplo utilizado é um código em produção que apresentou erro  foi aberto um Bug para correção.
Para garantir que a correção que eu iria implementar não ocasionasse outro erro, foi extraído o código legado para um objeto em C# e testar todos os cenários possíveis.

A imagem abaixo é o método do legado que foi convertido:

Está imagem era do legado, responsáveis pelo carregamento das parametrizações em cache do servidor:

Como visto na primeira imagem, o erro ocorria pelo fato que o parâmetro "CadastaroContaContabilDeClienteAutomaticamente" estava desativado, neste casso iria ocorrer o erro pois a variável "codigoDaContaContabilPadrao" tinha como valor padrão o "0", nisso ao consultar os dados pelo código da conta contábil iria retornar  um valor nulo, como não tinha essa validação era retornado uma exceção.

Visto que temos varias propriedades em vb.net, tendo como fazer vários cenários de teste, mais ai que vem "como que vou testar isso"? 

Vamos começar pelo simples, criar um objeto em C# com o intuito de fazer a "mesma coisa que este método faz", só que com outra visão.

Para facilitar os teste fiz o seguinte, criei  um objeto responsável por gerenciar os parâmetros e outro para fazer o que eu quero.

Até ai blz, mais e os teste e os mock?, certo para mockar eu preciso mudar a forma que os dados estão chegando e outra, terei que mudar essa busca de dados.

Para facilitar  a criação neste teste e em outros, posso criar um objeto responsável por fazer as buscas dos dados e criar uma interface do mesmo. Porque no momento que meu objeto não ter mais esta dependência e precisar somente de uma assinatura do objeto sendo a sua interface, irá facilitar muito a criação dos teste de unidade, porque assim eu consigo mockar os dados retornando o que eu querer do meu mock.

Um exemplo:

No exemplo acima eu instanciei o Mock assinando o tipo do meu objeto que quero mockar,
e inseri algumas regras: 
  • Ao chamar o método "BuscaMassaDeDados" e o parâmetro solicitado for "ConfiguracaoStoreProcedure" retorne este valor.
  • Ao chamar o método "BuscaMassaDeDados" e o parâmetro solicitado for "ConfiguracaoParaBuscaDeDados" e o nome da tabela for "t0151" retorne este valor.

Pode-se fazer muitas coisas com mock, você pode implementa a regra para retorno de dados para cada situação.

No meu caso, já tenho um objeto para consulta de dados e sua interface, sendo assim meu objeto refatorado irá pedir em seu construtor a interface do meu buscador de dados, já no caso do outro objeto responsável pelo controle dos parâmetros em cache  nomeado de "ParametrosContaContabilClienteFornecedor" também viria no meu construtor como parâmetro para preenchendo os atributos privados do meu objeto.

Ficando assim:

E o objeto ParametrosContaContabilClienteFornecedor ficaria assim:

Não deu para ver na imagem mais em seu construtor ele pede somente o código da empresa, que ao utilizar o atributo publico deste objeto ele ira fazer a consulta em cache no mesmo formato que fazia anteriormente no legado.

Olhando a imagem vocês irão ver que cada atributo publico do meu objeto tem vários teste, os teste deles são parecido com este:

Não tem segredo, não tem mock, simplesmente é um teste para validar o estado do meu atributo, até porque eu troco o valor do cache antes de obter o valor do atributo.

Os teste criado foram mais para validar o comportamento de cada atributo, ao realizar a trocar dos valores do meu cache.

Até porque quando se fala em cache de servidor ficamos meio com um pé atras, aquela "velha história, ta com erro, limpa o cache" quem nunca disse isso :).

O objeto final ficou assim:


E os teste ficaram assim:

Totalizou oito teste somente deste objeto. 
Como se pode ver no objeto tenho um método que retorna um outro objeto populado.
Tem o método que retorna um mock  do objeto "BuscadorDeDados", é um mock simples que ao executar o BuscaRegistro do meu objeto utilizando a parametrização ConfiguracaoParaBuscaDeDados, até porque o meu método tem mais de uma sobre carga.
Deve-se retornar os dados da contaContabilPai se a mesma não estiver nula.
Essa validação foi imposta pelo fato de se eu passar uma tabela vazia para validar no meu objeto deve retornar "null", não pode ocorrer uma exceção do mesmo formato que era no legado.

Já neste caso aqui: 

Eu quero validar se os dados da tabela como parâmetro para o objeto, retornou do jeito que eu quero, retornou com os dados corretos.

O legado ficou assim:

E assim:

Não é o cenário mais lindo do mundo, mais me da a certeza de garantia do resultado deste objeto, sabendo que oque está sendo retornado é o esperado.

Os teste criado para essa operação cobril todos os cenários possíveis.
No TDC que participei neste ano de 2016 em SP, ouvi na palestra de teste uma coisa muito importante. 

"Teve casos de empresa que decidiu em impôr a todos os programadores, que tudo que ia ser feito tanto como evolução quanto correção, teria que ser criado testes automatizados cobrindo aumentando a cobertura de teste."

Até a e tudo lindo, mais só que alguns programadores estava utilizando isso de má vontade.
"Criava se os teste passava o coverage e olhava se aquele objeto estava 100% coberto". 

Isto não é uma pratica muito saudável até porque 100%, "não quer dizer que está tudo testado".

Até porque, os teste criado pode não estar cobrindo todos os cenários importantes, "como alguma regra de negócio importante" isso provavelmente iria ser encontrado pelo cliente e retornaria como Bug porque não?.

Por este motivo eu pessoalmente adotei o seguinte quesito, o teste que estou criando vai cobrir que cenário?, "qual o valor desse teste para o produto?".

Por que se usa tempo e tempo é uma das coisas mais valiosa hoje em dia.

Então temos que pensar em tudo que fazemos e iremos fazer, se isso vale a pena ser feito se vai agregar valor.

Bom para finalizar,

Esse artigo foi mais para falar de como testar coisas pequenas como a correção de um Bug do legado, converte-lo e teste-lo. Uma das coisas que já ouvi falar muito é principalmente em correção é que "a correção é boboca não compensa criar teste, até porque converte ele vai dar trabalho". Blz mais quem vai garantir que essa correção não gerou outro bug?

Falei do básico de mock e teste unitário, isso é apenas um pingo de água no oceano do que pode ser feito, posso falar mais sobre, mais a frente de como criar um projeto do zero todo testado e como mockar tudo sem a necessidade de criar um teste de integração que é mais pesado e mais lento.

Então é isso obrigado, espero ter ajudado em alguma coisa, até mais....

8 comentários:

  1. Parabéns sr. Santiago.
    Existe uma polêmica no uso de mocks, mas eu vejo que em cenários legados ele é no mínimo um mal necessário.

    ResponderExcluir
    Respostas
    1. Obrigado :).
      Em breve estarei falando mais sobre como testar código legado utilizando object Fake.

      Excluir
  2. Parabéns Santiago. Excelente conteúdo que permite uma reflexão sobre o assunto.

    ResponderExcluir
  3. Nossa belo conteúdo, está bem elaborado parabéns.

    ResponderExcluir

Jonathan Dias Campos Santiago. Tecnologia do Blogger.