Imagine que você esteja fazendo um sistema que envia SMS. Você então precisa testar o envio. Seja para um volume pequeno, seja para um volume grande. Uma saída aceitável é desenvolver um teste unitário, que testa o envio, utilizando o próprio mecanismo fornecido pelo provedor de SMS. Porém, para cada SMS enviado, você terá que pagar as tarifas acordadas. Quanto mais você testar, mais você enviará e logo maior será sua conta no final do mês. O que fazer neste caso? Assumir o custo? Certamente o seu cliente não assumirá.
Como um bom desenvolvedor você certamente quer testar o que acontece com seu software em caso de falha do envio do SMS por parte da operadora, ou em caso de queda de todo o serviço. É impossível pensar em solicitar que a operadora interrompa por um tempo os serviços de envio de SMS para que você possa testar seu sistema. O que fazer então?
Uma das soluções para todos estes problemas é utilizar objetos Mock. Objetos Mock simulam comportamentos de objetos “reais”, de forma controlada pelo desenvolvedor. É possível criar um Mock do objeto de envio de SMS, que terá todos os métodos e parâmetros idênticos ao original, porém não fará efetivamente o envio. Logo você poderá testar, sem efetivamente pagar e ainda poderá simular queda do serviço por parte da operadora.
Mock, TDD e base de dados.
Metodologias de desenvolvimento orientado a teste, o TDD, faz com que o teste unitário seja executado inúmeras vezes durante o desenvolvimento ou quando há alguma alteração no código em período de manutenção e suporte. Isto obriga que o teste unitário seja rápido, para que não se torne inviável. Um TDD ideal é aquele que além de ser rápido, não obriga uma configuração inicial da aplicação e possa ser testado de forma isolada.
Uma das questões mais delicadas no TDD é quando temos que testar contra uma base de dados. Um dos princípios do bom TDD é que ele deve permitir testar o código sem o uso de recursos externos, e permitir testar de uma maneira “desplugada”, ou seja, se você estiver no meio do deserto só com seu note book, sem internet, e precisar testar o código, deve ser possível.
Embora possível e muito utilizado, testes unitários que conectam diretamente na base de dados pode consumir muito recurso, como de rede e performance, se pensarmos que os testes serão executadas centenas, se não milhares de vezes pela equipe de desenvolvimento. Quanto maior a carga que queremos testar, maior o consumo de recursos e mais tempo de teste será necessário.
Tendo uma base de dados, utilizada somente para o teste unitário pode ainda gerar alguns outros problemas. Suponha que dois desenvolvedores estejam realizando o teste ao mesmo tempo. Cenário provável sabendo que os testes são realizados a cada mudança de código. As operações realizadas por cada um deles, ao mesmo tempo, podem levantar um erro de teste, que na realidade só existiu por causa do ambiente de teste. Um exemplo é se um destes programadores estiver inserindo registros, e outro apagando. Provavelmente eles terão um conflito e desprenderão tempo em resolver um erro “inexistente”, gerando ainda mais custo e comprometendo o prazo.
Os objetos mocks certamente são uma grande ferramenta para contornar estas situações. Eles permitem realizar testes de forma desconectada, como por exemplo, criando objetos “Stubs”. Stubs são objetos criados para debug, que possuem valores simulados que permitam realizar certos testes. Suponha que você queira validar o envio de SMS. Ao invés de consultar um número telefônico do usuário direto na base de dados, você pode criar um Stub da sua classe de usuários, e utilizar dados fictícios. Porém ao utilizar dados fictícios para enviar SMS através do seu provedor, certamente irá gerar um erro. Neste caso você terá que criar um objeto que simula o envio do SMS, o objeto Mock.
Uma outra grande funcionalidade dos objetos mocks é quando trabalhamos com classes de Mapeamento objecto-relacional (ou ORM), como por exemplo, o Hibernate e o OJB para java, ou o N-Hibernate para .NET ou até o SQLObject para o Python.
Quando trabalhamos com ORM, os mock-object nos auxiliam a testar as operações de forma desconectada, e validar se as operações estão sendo chamadas a contento.
Verificações de estado.
Com objetos mock, é possível fazer verificações de estado, como por exemplo, efetuar simulações de um recurso externo, como um webservice. Neste caso, para uma simulação você pode criar um “stub”, ou seja, um objeto que simula o webservice, com os mesmos métodos e parâmetros. As verificações de estado permitem que você veja se um resultado foi o esperado, com métodos como o Assert, do teste unitário.
Suponha que o seu webservice faça uma multiplicação de dois números passados por parâmetro. Ao enviar 4 e 5 o resultado esperado é 20. Criando um objeto mock, você pode simular o webservice, garantindo que a sua parte do código esteja correta. Com o método assert.areequal(20, resultado do webservice [mock]), é efetuado uma verificação de estado.
Verificações de comportamento.
É comum, em desenvolvimento TDD, precisar validar se uma seqüência de operações está sendo executada de forma esperada. Tomando por base uma loja virtual, é imperativo testar se em um pedido da loja, os objetos estão efetuando uma série de operações seqüenciais como: Validar dados do cliente, consultar estoque do pedido, montar lista do pedido, dar baixa no estoque e iniciar transação no cartão de crédito. Neste caso, alguns objetos mock (Rhino mock do .Net por exemplo) podem nos auxiliar a fazer este tipo de validação. Note que diferente da validação de estado, nenhum resultado final é esperado. No exemplo anterior o resultado esperado era 20. Neste caso não existe o resultado final único esperado, mas sim uma seqüência de operações que ocorrem de forma satisfatória.
Principais Mocks
Separei uma lista dos principais mocks, com suas respectivas linguagens.
Ruby
- Mocha [http://mocha.rubyforge.org/]
- RSpec [http://rspec.rubyforge.org/]
- FlexMock [http://onestepback.org/software/flexmock/]
.NET
- Rhino Mocks [http://www.ayende.com/projects/rhino-mocks.aspx]
- NMock [http://nmock.org/]
- Moq [http://code.google.com/p/moq/]
- Typemock [http://www.typemock.com/]
- Attach [http://www.codeplex.com/attach]
Java
- jMock [http://www.jmock.org/]
- EasyMock [http://www.easymock.org/]
- rMock [http://rmock.sourceforge.net/]
- SevenMock [http://seven-mock.sourceforge.net/]
- Mockito [http://code.google.com/p/mockito/]
PERL
- Test::MockObject [http://search.cpan.org/dist/Test-MockObject/lib/Test/MockObject.pm]
SmallTalk
- SMock [http://www.macta.f2s.com/Thoughts/smock.html]
Pascal / Delphi
- Pascal Mock [http://sourceforge.net/projects/pascalmock/]
C++
- MockPP [http://mockpp.sf.net/]
- amop [http://code.google.com/p/amop]
- MockItNow [http://code.google.com/p/mockitnow]
ActionScript
- As2lib Mock Object [http://www.as2lib.org/]
PHP
- SimpleTest [http://simpletest.org/]
- Yay! Mock [http://yaymock.googlecode.com/]
Conclusão
Cada vez mais nós desenvolvedores estamos pressionados a desenvolver mais rápido, e sem erro. Os objetos mocks com certeza é uma excelente ferramenta e que deve ser incorporado a qualquer desenvolvimento a fim de testar o código de forma isolada e segura.
Forte Abraço!