utilização de mock objects em testes unitários
DESCRIPTION
Apresentação do Nuno Caneco sobre Utilização de Mock Objects em Testes Unitários na 29a Reunião Presencial da Comunidade NetPonto em Lisboa (http://netponto.org).TRANSCRIPT
Utilização de Mock Objects em Testes UnitáriosNuno Caneco
http://netponto.org29ª Reunião Lisboa - 26/05/2012
Patrocinador “GOLD”
Patrocinadores “Silver”
Nuno CanecoConsultor Sénior na |create|it|
Áreas de interesse:• SOA e Integração• WCF• desenvolvimento core • Dependency Injection.
Agenda
• Unit tests• Mocking frameworks• Demo• Considerações de arquitectura• Dicas
Testes e mais testes…
Testes de carga
Testes unitários
Testes funcionais
Testes de sistema
Testes de integração
Testes de aceitação
Testes de performance
Testes de segurança
Testes de usabilidade
Testes unitários• Objectivo:– Testar a lógica de um método de forma isolada, tendo em
conta os vários inputs possíveis e tendo em atenção casos de fronteira e valores inválidos.
• Características:– Repetíveis – Idempotentes– Não depender de outros testes– Inócuos
Exemplo [TestClass] public class SaleRecordsReaderTest { private SaleRecordsReader GetReader() { return new SaleRecordsReader(); }
[TestMethod] public void ListSaleRecordTest() { var reader = GetReader();
var saleRecords = reader.ListSaleRecords();
Assert.IsNotNull(saleRecords); Assert.IsTrue(saleRecords.Count > 0); } }
Alguns impedimentos e dificuldades
• UI: É difícil de testar código de interface– HTML/Javascript
• Acessibilidade– dos métodos: private, protected– da classe: internal
• Dependências– Persistência: BD, Lista de SharePoint– Sistemas externos– Utilização de providers: Membership providers, …
• Classes binded a determinados contextos– Ex.: CodeBehind de páginas .ASPX
Mitos• “O meu código é perfeito!! Não é preciso testar.”
• “Não vale a pena fazer testes unitários. Basta clicar naquele link e pronto… Está testado!”
• “O projecto precisa de tempo para fazer testes unitários”
• “Eh pá… tenho que ter uma BD com dados de teste…”
• “Tenho muitas dependências. Não é possível abstrair-me dos sistemas externos”
Mitos• “O meu código é perfeito!! Não é preciso testar.”
• “Não vale a pena fazer testes unitários. Basta clicar naquele link e pronto… Está testado!”
• “O projecto precisa de tempo para fazer testes unitários”
• “Eh pá… tenho que ter uma BD com dados de teste…”
• “Tenho muitas dependências. Não é possível abstrair-me dos sistemas externos”
Motivação
Sistemas externosBD
Serviços
Business
DAL/RAL
Sistema
Mocks com unit testsIn object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways.
A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.
http://en.wikipedia.org/wiki/Mock_object
Estratégia de implementação1. Gerar mock
– Usar framework de mocking para gerar mocks das dependências da classe a testar
2. Substituir dependência pelo mock– Inicializar classe a ser testada com o mock em substituição da implementação “real”
3. Programa o comportamento do mock– Programar os mocks para retornar determinados outputs em função dos inputs
• Valores de retorno• Excepções
4. Aferir o comportamento da classe em teste
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
CustomerReaderMock invoiceReaderMock
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
CustomerReaderMock invoiceReaderMock
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
CustomerReaderMock invoiceReaderMock
E que tal um exemplo?
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
CustomerReaderMock invoiceReaderMock
Os mocks substituem as classes reais no contexto de um teste unitário
O objectivo• Evitar dependências– Dados (base-de-dados, configuração, …)– Estado de sistemas externos (CRM, ERP, …)– Contas de utilizador– Setup de ambientes com dados de teste
• Testar vários cenários de negócio e condições de fronteira– Nulos, valores inválidos, decisões em função de valores
específicos
Aplicabilidade
• A todo o código C# que seja testável–Serviços WCF–Aplicações MVC–Aplicações WPF–…
Frameworks de mocking • Microsoft Moles (Fakes no VS11)• NMock• EasyMock.NET• TypeMock Isolator (Commercial / Paid)• Rhino Mocks• Moq• NSubstitute• JustMock (Commercial / Paid)• FakeItEasy
Caso de uso
https://github.com/nmcc/unit-test-mock-demo
demonstração
Questões?
Considerações de arquitectura• É necessário fazer override do método .Equals() para os
objectos de input dos métodos
• Construtor que receba as interfaces das dependências
• Todas as dependências devem ser declaradas como interfaces– Usar “Ctrl+R, I” para gerar interfaces automaticamente
• É conveniente que os testes herdem de uma classe base que contém os mocks
Dependency Injection
• Substituição das dependências é automática
• Evita múltiplos construtores das classes a testar
• Código de inicialização do container fica na classe base de testes
Técnica: Mocks de dependências directas
CustomerManager
ICustomerReader
CustomerReader
IInvoiceReader
InvoiceReader
CustomerManagerTest
CustomerReaderMock InvoiceReaderMock
Técnica:Mock das fronteiras
Sistemas externosBD
Serviços
Business
DAL/RAL
Sistema
Dicas• Classe de teste junto à classe testada
– Ajuda a lembrar que existem testes – Utilizar #if para não compilar testes em Release
• Mock das fronteiras da aplicação– Permite atingir coverage elevada com menor número de testes
• Usar uma classe base para declarar os mocks
• Usar dependency injection– Permite injectar automaticamente os mocks numa classe base– Evitando a repetição da criação dos mocks pelas várias classes de teste
Prós e contras
+ confiança no código + confiança em
refactorizações Minimiza testes funcionais Utilização de mocks
“descomplica” testes unitários Execução automatizada com
integração contínua
Testes têm que ser mantidos O código pode ter que ser
estruturado para ser testável Nem todo o código é testável
ReferênciasRhino mocks– http://hibernatingrhinos.com/open-source/rhino-mocks
Demo– https://github.com/nmcc/unit-test-mock-demo
Patrocinador “GOLD”
Patrocinadores “Silver”
Questões?
Próximas reuniões presenciais
• 26-05-2012 – Maio• 02-06-2012 – Junho (Coimbra)• 16-06-2012 – Junho• 21-07-2012 – Julho
Reserva estes dias na agenda! :)
Obrigado!
Nuno Caneco
Mail/MSN: [email protected]: http://blogit.create.pt/blogs/nunocaneco/Twitter: @NunoCanecoGamertag: nunocaneco