Injeção de Dependência e
    Testes com Dublês
                  2º Locaweb Tec Day
                            04.02.2009
Conteúdo

• Exemplo
• Injeção de Dependência (DI)
• Testes com Dublês (Doubles)
Exemplo
public List<Phone> phonesForCompany(String name) {
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();
    for (Phone phone: phones) {
        Phone phone = it.next();
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                     3
Continuando o Exemplo


• Criando interface
   – public interface PhoneFinder {
         List<Phone> findAll();
     }


• Criação de instância continua obrigatória
   – public class PhoneLister {
         private PhoneFinder finder;
         public PhoneLister() {
            finder = new MySqlPhoneFinder();
         }
     }




                                               4
Dependência


• PhoneLister depende da interface e da implementação
• Como depender somente da interface?




                                                        5
Idéia Básica - Injeção




                         Injeção


                                   Framework
                                   Injeção de
                                   Dependência




                                                 6
Tipos de Injeção


• Injeção Por Construtor
   public PhoneLister(PhoneFinder finder) {
     this.finder = finder;
   }


• Por Setter
  class PhoneLister {
    public PhoneLister(){…}
    public void setPhoneFinder(PhoneFinder finder) {
       this.finder = finder;
    }
  }




                                                       7
XML
<objects>
  <object id=“myFinder”
          class="br.com.locaweb.pabx.MySqlPhoneFinder">
  </object>


  <object id=“phoneLister”
          class="br.com.locaweb.pabx.PhoneLister">
      <property name=“phoneFinder" ref=“myFinder"/>
  </object>
</objects>




                                                          8
Usando na aplicação
public void test() {
 Repository repository = new Repository("objects.xml");
 PhoneLister lister = repository.object(“phoneLister");
 List phones = lister.phonesForCompany("LW Telecom");
 assertEquals(“1135443500",phones.get(0).getNumber());
}




                                                          9
Fábrica de Objetos

• Singleton
   – Instância única compartilhada do objeto
   – Uso padrão, mais comum
   – Objetos de serviço sem estado (stateless)
• Protótipos
   – Cada chamada cria um novo objeto
• Escopos de Objetos Customizados
   – Objetos armazenados fora do controle do container (ex: request,
     session em uma aplicação Web)




                                                                       10
Fábrica de Objetos




                     10
Fábrica de Objetos

                     Repositório


                        S          P




      Cliente 1                        Cliente 2




                                                   10
Fábrica de Objetos

                     Repositório


                        S          P




      Cliente 1                        Cliente 2


        S1




                                                   10
Fábrica de Objetos

                       Repositório


                          S          P




      Cliente 1                          Cliente 2


        S1        S1




                                                     10
Fábrica de Objetos

                       Repositório


                          S          P




      Cliente 1                          Cliente 2


        S1        S1             S1




                                                     10
Fábrica de Objetos

                            Repositório


                               S          P




      Cliente 1                               Cliente 2


        S1        S1   P1             S1




                                                          10
Fábrica de Objetos

                            Repositório


                               S          P




      Cliente 1                                Cliente 2


        S1        S1   P1             S1      P2




                                                           10
Fábrica de Objetos

                            Repositório


                               S          P




      Cliente 1                                Cliente 2


        S1        S1   P1             S1      P2    P3




                                                           10
Vantagens de usar Injeção de Dependência




                                           11
Vantagens de usar Injeção de Dependência

• Ajuda a escrever código que é fácil de testar: é só criar
  objetos e atribuir as propriedades desejadas usando os
  setters




                                                              11
Vantagens de usar Injeção de Dependência

• Ajuda a escrever código que é fácil de testar: é só criar
  objetos e atribuir as propriedades desejadas usando os
  setters
• Facilita boas práticas de programação: uso de interfaces no
  lugar de classes




                                                                11
Vantagens de usar Injeção de Dependência

• Ajuda a escrever código que é fácil de testar: é só criar
  objetos e atribuir as propriedades desejadas usando os
  setters
• Facilita boas práticas de programação: uso de interfaces no
  lugar de classes
• Não envasivo: o código do sistema depende o mínimo
  possível da API do Framework




                                                                11
Vantagens de usar Injeção de Dependência

• Ajuda a escrever código que é fácil de testar: é só criar
  objetos e atribuir as propriedades desejadas usando os
  setters
• Facilita boas práticas de programação: uso de interfaces no
  lugar de classes
• Não envasivo: o código do sistema depende o mínimo
  possível da API do Framework
• Dependências são explícitas e evidentes




                                                                11
Vantagens de usar Injeção de Dependência

• Ajuda a escrever código que é fácil de testar: é só criar
  objetos e atribuir as propriedades desejadas usando os
  setters
• Facilita boas práticas de programação: uso de interfaces no
  lugar de classes
• Não envasivo: o código do sistema depende o mínimo
  possível da API do Framework
• Dependências são explícitas e evidentes
• Como os componentes não precisam procurar
  colaboradores em tempo de execução, o código fica mais
  fácil de escrever e manter


                                                                11
Frameworks para todas as linguagens




                                      12
Frameworks para todas as linguagens


• Java - Spring, PicoContainer, HiveMind




                                           12
Frameworks para todas as linguagens


• Java - Spring, PicoContainer, HiveMind
• PHP - DIContainer




                                           12
Frameworks para todas as linguagens


• Java - Spring, PicoContainer, HiveMind
• PHP - DIContainer
• .NET - Spring.NET, PicoContainer.NET




                                           12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python




                                             12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python
•   Ruby - Needle




                                             12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python
•   Ruby - Needle
•   Perl - IOC Module




                                             12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python
•   Ruby - Needle
•   Perl - IOC Module
•   Flex - Flicc




                                             12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python
•   Ruby - Needle
•   Perl - IOC Module
•   Flex - Flicc




                                             12
Frameworks para todas as linguagens


•   Java - Spring, PicoContainer, HiveMind
•   PHP - DIContainer
•   .NET - Spring.NET, PicoContainer.NET
•   Python - PyContainer, Spring Python
•   Ruby - Needle
•   Perl - IOC Module
•   Flex - Flicc

•   mais em http://en.wikipedia.org/wiki/Dependency_injection



                                                                12
DI – Linha do Tempo
Construtor ou Setter?
Construtor ou Setter?

• Construtor com parâmetros deixa claro o que é preciso
  para criar o objeto
Construtor ou Setter?

• Construtor com parâmetros deixa claro o que é preciso
  para criar o objeto
• Usando construtor evita campos imutáveis de serem
  alterados
Construtor ou Setter?

• Construtor com parâmetros deixa claro o que é preciso
  para criar o objeto
• Usando construtor evita campos imutáveis de serem
  alterados
• Cuidado: construtores com muitos parâmetros podem ser
  um indicativo de objeto com responsabilidades demais
Construtor ou Setter?

• Construtor com parâmetros deixa claro o que é preciso
  para criar o objeto
• Usando construtor evita campos imutáveis de serem
  alterados
• Cuidado: construtores com muitos parâmetros podem ser
  um indicativo de objeto com responsabilidades demais
• Construtor é ruim se tiver parâmetros simples como
  Strings: com setter você cria um método que identifica o
  que a string significa
Construtor ou Setter?

• Construtor com parâmetros deixa claro o que é preciso
  para criar o objeto
• Usando construtor evita campos imutáveis de serem
  alterados
• Cuidado: construtores com muitos parâmetros podem ser
  um indicativo de objeto com responsabilidades demais
• Construtor é ruim se tiver parâmetros simples como
  Strings: com setter você cria um método que identifica o
  que a string significa
• Receita geral: comece com construtor e mude para setter
  se a coisa ficar complicada demais
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();
    for (Phone phone: phones) {
        Phone phone = it.next();
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                     15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();
    for (Phone phone: phones) {
        Phone phone = it.next();
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                     15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();
    for (Phone phone: phones) {
                                   Preencher os dados
        Phone phone =   it.next(); no DB manualmente
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                        15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();             E se eu não tiver
                                                        acesso ao banco?
    for (Phone phone: phones) {
                                   Preencher os dados
        Phone phone =   it.next(); no DB manualmente
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                                            15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();             E se eu não tiver
                                                        acesso ao banco?
    for (Phone phone: phones) {
                                   Preencher os dados
        Phone phone =   it.next(); no DB manualmente    E se ele for lento
                                                            demais?
        if (!phone.getCompany().equals(name)) {
            phones.remove(phone);
        }
    }
    return phones;
}

                                                                             15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();             E se eu não tiver
                                                        acesso ao banco?
    for (Phone phone: phones) {
                                   Preencher os dados
        Phone phone =   it.next(); no DB manualmente    E se ele for lento
                                                            demais?
        if (!phone.getCompany().equals(name)) {
                                                         E se o custo for
            phones.remove(phone);                             alto?
        }
    }
    return phones;
}

                                                                             15
Voltando ao nosso PhoneLister
public List<Phone> phonesForCompany(String name) {
                       Finder
    List<Phone> phones = finder.findAll();
    Iterator<Phone> it = phones.iterator();             E se eu não tiver
                                                        acesso ao banco?
    for (Phone phone: phones) {
                                   Preencher os dados
        Phone phone =   it.next(); no DB manualmente    E se ele for lento
                                                            demais?
        if (!phone.getCompany().equals(name)) {
                                                         E se o custo for
            phones.remove(phone);                             alto?
        }
                                                         E se não tiver
    }                                                    banco ainda?

    return phones;
}

                                                                             15
Testes com Dublês (Doubles)


                  Double




                              16
Testes com Dublês (Doubles)


                  Double




                              16
Testes com Dublês (Doubles)


                   Double




Dummy
Object




                               16
Testes com Dublês (Doubles)


                   Double




Dummy
          Stub
Object




                               16
Testes com Dublês (Doubles)


                   Double




Dummy
          Stub      Spy
Object




                               16
Testes com Dublês (Doubles)


                   Double




Dummy
          Stub      Spy        Mock
Object




                                      16
Testes com Dublês (Doubles)


                   Double




Dummy
          Stub      Spy        Mock   Fake
Object




                                             16
Dummy Object

•   Objetos que nunca são usados, só servem para preencher parâmetros




                                                                        17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
          Product product = new Product("Dummy Product");
          State state = new State("West Dakota", "WD");
          City city = new City("Centreville", state);
          Address address = new Address("123 Blake St.", city, "12345");
          Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
    Dummy Product product = new Product("Dummy Product");
          State state = new State("West Dakota", "WD");
          City city = new City("Centreville", state);
          Address address = new Address("123 Blake St.", city, "12345");
          Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
    Dummy Product product = new Product("Dummy Product");
    Dummy State state = new State("West Dakota", "WD");
          City city = new City("Centreville", state);
          Address address = new Address("123 Blake St.", city, "12345");
          Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
    Dummy Product product = new Product("Dummy Product");
    Dummy State state = new State("West Dakota", "WD");
    Dummy City city = new City("Centreville", state);
          Address address = new Address("123 Blake St.", city, "12345");
          Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
    Dummy Product product = new Product("Dummy Product");
    Dummy State state = new State("West Dakota", "WD");
    Dummy City city = new City("Centreville", state);
    Dummy Address address = new Address("123 Blake St.", city, "12345");
          Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Dummy Object

•    Objetos que nunca são usados, só servem para preencher parâmetros

    public void testInvoice_addLineItem_noECS() {
          final int QUANTITY = 1;
    Dummy Product product = new Product("Dummy Product");
    Dummy State state = new State("West Dakota", "WD");
    Dummy City city = new City("Centreville", state);
    Dummy Address address = new Address("123 Blake St.", city, "12345");
    Dummy Customer customer= new Customer("Dummy Customer");
          Invoice inv = new Invoice(customer);
          // Add an item to the invoice
          inv.addItemQuantity(product, QUANTITY);
          // Verify that item was inserted
          List lineItems = inv.getLineItems();
          assertEquals("number of items", lineItems.size(), 1);
          LineItem actual = (LineItem)lineItems.get(0);
          LineItem expItem = new LineItem(inv, product, QUANTITY);
          assertLineItemsEqual("",expItem, actual);
       }


                                                                           17
Stub

•   Provê alguns dados estáticos que serão usados nos testes
•   Não funciona para outras coisas além do que está no teste




                                                                18
Stub

•   Provê alguns dados estáticos que serão usados nos testes
•   Não funciona para outras coisas além do que está no teste

class OrderStateTester {
    public void testOrderSendsMailIfUnfilled() {
        Order order = new Order(TALISKER, 51);
        order.fill(new Customer("techday@locaweb.com.br"));
        assertEquals("techday@locaweb.com.br", order.getEmail());
    }
}




                                                                    18
Spy
•   Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas




                                                                                 19
Spy
•   Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas
class OrderStateTester {
    public void testOrderSendsMailIfUnfilled() {
	        Order order = new Order(TALISKER, 51);
	        MailServiceSpy mailer = new MailServiceSpy();
	        order.setMailer(mailer);
	        order.fill(new Customer("techday@locaweb.com.br"));
	        assertEquals(1, mailer.numberSent());
    }
}




                                                                                 19
Spy
•   Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas
class OrderStateTester {
    public void testOrderSendsMailIfUnfilled() {
	        Order order = new Order(TALISKER, 51);
	        MailServiceSpy mailer = new MailServiceSpy();
	        order.setMailer(mailer);
	        order.fill(new Customer("techday@locaweb.com.br"));
	        assertEquals(1, mailer.numberSent());
    }
}

public class MailServiceSpy implements MailService {
    private List<Message> messages = new ArrayList<Message>();
    public void send(Message msg) {
	        messages.add(msg);
    }
    public int numberSent() {
	        return messages.size();
    }
}

                                                                                 19
O que são Mock Objects?




   Years later, Mock objects are still quite controversial, often
           misused and sometimes misunderstood.

       “Pintside Thoughts: Mock Objects History”

                      —Tim McKinnon
O que são Mock Objects?


• Cada pessoa entende de um jeito diferente
• Terminologia ambígua
• Ficou famoso inicialmente na comunidade JAVA & TDD


   Comportamento                       Estado
   testa interação entre       testa o resultado dessas
           objetos                    interações
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
        expect(ftpClient.logout()).andReturn(true);
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");
        uploader.sendFTPFiles(fileNames);

        verify(ftpUtils, ftpClient);
    }

}
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
                                                               Criação dos Mocks
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
        expect(ftpClient.logout()).andReturn(true);
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");
        uploader.sendFTPFiles(fileNames);

        verify(ftpUtils, ftpClient);
    }

}
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
                                                               Criação dos Mocks
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
                                                                         Gravando
        expect(ftpClient.logout()).andReturn(true);
                                                                  Comportamentos
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");
        uploader.sendFTPFiles(fileNames);

        verify(ftpUtils, ftpClient);
    }

}
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
                                                               Criação dos Mocks
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
                                                                         Gravando
        expect(ftpClient.logout()).andReturn(true);
                                                                  Comportamentos
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);    Fim gravação

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");
        uploader.sendFTPFiles(fileNames);

        verify(ftpUtils, ftpClient);
    }

}
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
                                                               Criação dos Mocks
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
                                                                         Gravando
        expect(ftpClient.logout()).andReturn(true);
                                                                  Comportamentos
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);    Fim gravação

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");                     Execução do método
        uploader.sendFTPFiles(fileNames);                          a ser testado

        verify(ftpUtils, ftpClient);
    }

}
Código Mock
public class FTPUploaderTest {
    public void shouldUploadOnePhoneConfig() throws Exception {
        FTPUtils ftpUtils = createMock(FTPUtils.class);
        FTPUploader uploader = new FTPUploader(ftpUtils);
                                                               Criação dos Mocks
        FTPClient ftpClient = createMock(FTPClient.class);

        expect(ftpUtils.getFTPConnection()).andReturn(ftpClient);
        ftpUtils.delete(ftpClient, "/", "file-name.cfg");
        ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg");
                                                                         Gravando
        expect(ftpClient.logout()).andReturn(true);
                                                                  Comportamentos
        expect(ftpClient.isConnected()).andReturn(false);
        expect(ftpClient.disconnect());
        replay(ftpUtils, ftpClient);    Fim gravação

        List<String> fileNames = new ArrayList<String>();
        fileNames.add("file-name.cfg");                     Execução do método
        uploader.sendFTPFiles(fileNames);                          a ser testado

        verify(ftpUtils, ftpClient);     Verificação Final
    }

}
Fake Object

•   Têm uma implementação completa, mas simples
•   Não vai para produção
•   Exemplo: banco de dados em memória




                                                  23
Fake Object

    •   Têm uma implementação completa, mas simples
    •   Não vai para produção
    •   Exemplo: banco de dados em memória
    public void testReadWrite_inMemory() throws Exception {
	        // Setup:
	        FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl();
	        facade.setDao(new InMemoryDatabase());
	        // Exercise:
	        BigDecimal yyc = facade.createAirport("YYC", "Calgary", "Calgary");
	        BigDecimal lax = facade.createAirport("LAX", "LAX Intl", "LA");
	        facade.createFlight(yyc, lax);
	        List flights = facade.getFlightsByOriginAirport(yyc);
	        // Verify:
	        assertEquals("# of flights", 1, flights.size());
	        Flight flight = (Flight) flights.get(0);
	        assertEquals("origin", yyc, flight.getOrigin().getCode());
    }



                                                                               23
Fake Object

    •   Têm uma implementação completa, mas simples
    •   Não vai para produção
    •   Exemplo: banco de dados em memória
    public void testReadWrite_inMemory() throws Exception {
	        // Setup:
	        FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl();
	        facade.setDao(new InMemoryDatabase());
	        // Exercise:            Fake
	        BigDecimal yyc = facade.createAirport("YYC", "Calgary", "Calgary");
	        BigDecimal lax = facade.createAirport("LAX", "LAX Intl", "LA");
	        facade.createFlight(yyc, lax);
	        List flights = facade.getFlightsByOriginAirport(yyc);
	        // Verify:
	        assertEquals("# of flights", 1, flights.size());
	        Flight flight = (Flight) flights.get(0);
	        assertEquals("origin", yyc, flight.getOrigin().getCode());
    }



                                                                               23
Implementação de Fake Object
public class InMemoryDatabase implements FlightDao{
  private List airports = new Vector();

    public Airport createAirport(String airportCode, String name, String nearbyCity)
       	                        throws DataException, InvalidArgumentException {
       Airport result = new Airport(getNextAirportId(), airportCode, name,
       createCity(nearbyCity));
       airports.add(result);
       return result;
    }

    public Airport getAirportByPrimaryKey(BigDecimal airportId) throws DataException{
       for (Airport airport: airports) {
          if (airport.getId().equals(airportId)) {
            return airport;
          }
       }
       throw new DataException("Airport not found:"+airportId);
    }
}


                                                                                  24
Mocks nas Linguagens
Mocks nas Linguagens


• Java - EasyMock, JMock, Mockito
Mocks nas Linguagens


• Java - EasyMock, JMock, Mockito
• Ruby - Mocha, RSpec?
Mocks nas Linguagens


• Java - EasyMock, JMock, Mockito
• Ruby - Mocha, RSpec?
• .NET - NMock, Rhino Mocks
Mocks nas Linguagens


•   Java - EasyMock, JMock, Mockito
•   Ruby - Mocha, RSpec?
•   .NET - NMock, Rhino Mocks
•   Perl - Test::MockObjects
Mocks nas Linguagens


•   Java - EasyMock, JMock, Mockito
•   Ruby - Mocha, RSpec?
•   .NET - NMock, Rhino Mocks
•   Perl - Test::MockObjects
•   C++ - Google Mocks
Mocks nas Linguagens


•   Java - EasyMock, JMock, Mockito
•   Ruby - Mocha, RSpec?
•   .NET - NMock, Rhino Mocks
•   Perl - Test::MockObjects
•   C++ - Google Mocks
•   PHP - Simple Test for PHP
Testes com Doubles - Recapitulando


                   Double




Dummy
          Stub      Spy      Mock     Fake
Object




                                             26
Testes com Doubles - Recapitulando


                    Double




Dummy
             Stub    Spy      Mock     Fake
Object



 Preenche
parâmetros




                                              26
Testes com Doubles - Recapitulando


                          Double




Dummy
               Stub        Spy     Mock   Fake
Object



                Provê
 Preenche
             dados para
parâmetros
              os testes




                                                 26
Testes com Doubles - Recapitulando


                          Double




Dummy
               Stub        Spy      Mock   Fake
Object



                Provê      Provê
 Preenche
             dados para   dados e
parâmetros
              os testes    grava




                                                  26
Testes com Doubles - Recapitulando


                          Double




Dummy
               Stub        Spy       Mock        Fake
Object



                Provê      Provê      Verifica
 Preenche
             dados para   dados e   comporta-
parâmetros
              os testes    grava     mentos




                                                        26
Testes com Doubles - Recapitulando


                          Double




Dummy
               Stub        Spy       Mock         Fake
Object



                Provê      Provê      Verifica   Implemen-
 Preenche
             dados para   dados e   comporta-      tação
parâmetros
              os testes    grava     mentos       +simples




                                                             26
Quiz




Dublês + Injeção de Dependência =
Quiz




Dublês + Injeção de Dependência = Testes
Referências


•   http://www.springframework.org/documentation
•   http://martinfowler.com/articles/injection.html
•   http://martinfowler.com/articles/mocksArentStubs.html
•   http://xunitpatterns.com/
•   Anil Hemrajani, Agile Java Development with Spring, Hibernate and
    Eclipse
Para aqueles que buscam sucesso em projetos e negócios, LOCAWEB oferece soluções
        inovadoras em Hosted IT Services de alta performance e confiabilidade.




                Data Center - Av. Juscelino Kubitschek, 1830, 10º andar – Itaim Bibi – São Paulo – SP

        Escritório SP - Av. Juscelino Kubitschek, 1830, 10º andar – Itaim Bibi – São Paulo – SP | (11) 3544-0400

         Escritório RS - Av. Carlos Gomes, 1340, 6º andar – Auxiliadora – Porto Alegre – RS | (51) 4062-0203

                                           0800 726 7770 - Demais Estados

Injeção de Dependência e Testes com Dublês

  • 1.
    Injeção de Dependênciae Testes com Dublês 2º Locaweb Tec Day 04.02.2009
  • 2.
    Conteúdo • Exemplo • Injeçãode Dependência (DI) • Testes com Dublês (Doubles)
  • 3.
    Exemplo public List<Phone> phonesForCompany(Stringname) { List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); for (Phone phone: phones) { Phone phone = it.next(); if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 3
  • 4.
    Continuando o Exemplo •Criando interface – public interface PhoneFinder { List<Phone> findAll(); } • Criação de instância continua obrigatória – public class PhoneLister { private PhoneFinder finder; public PhoneLister() { finder = new MySqlPhoneFinder(); } } 4
  • 5.
    Dependência • PhoneLister dependeda interface e da implementação • Como depender somente da interface? 5
  • 6.
    Idéia Básica -Injeção Injeção Framework Injeção de Dependência 6
  • 7.
    Tipos de Injeção •Injeção Por Construtor public PhoneLister(PhoneFinder finder) { this.finder = finder; } • Por Setter class PhoneLister { public PhoneLister(){…} public void setPhoneFinder(PhoneFinder finder) { this.finder = finder; } } 7
  • 8.
    XML <objects> <objectid=“myFinder” class="br.com.locaweb.pabx.MySqlPhoneFinder"> </object> <object id=“phoneLister” class="br.com.locaweb.pabx.PhoneLister"> <property name=“phoneFinder" ref=“myFinder"/> </object> </objects> 8
  • 9.
    Usando na aplicação publicvoid test() { Repository repository = new Repository("objects.xml"); PhoneLister lister = repository.object(“phoneLister"); List phones = lister.phonesForCompany("LW Telecom"); assertEquals(“1135443500",phones.get(0).getNumber()); } 9
  • 10.
    Fábrica de Objetos •Singleton – Instância única compartilhada do objeto – Uso padrão, mais comum – Objetos de serviço sem estado (stateless) • Protótipos – Cada chamada cria um novo objeto • Escopos de Objetos Customizados – Objetos armazenados fora do controle do container (ex: request, session em uma aplicação Web) 10
  • 11.
  • 12.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 10
  • 13.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 10
  • 14.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 S1 10
  • 15.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 S1 S1 10
  • 16.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 S1 P1 S1 10
  • 17.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 S1 P1 S1 P2 10
  • 18.
    Fábrica de Objetos Repositório S P Cliente 1 Cliente 2 S1 S1 P1 S1 P2 P3 10
  • 19.
    Vantagens de usarInjeção de Dependência 11
  • 20.
    Vantagens de usarInjeção de Dependência • Ajuda a escrever código que é fácil de testar: é só criar objetos e atribuir as propriedades desejadas usando os setters 11
  • 21.
    Vantagens de usarInjeção de Dependência • Ajuda a escrever código que é fácil de testar: é só criar objetos e atribuir as propriedades desejadas usando os setters • Facilita boas práticas de programação: uso de interfaces no lugar de classes 11
  • 22.
    Vantagens de usarInjeção de Dependência • Ajuda a escrever código que é fácil de testar: é só criar objetos e atribuir as propriedades desejadas usando os setters • Facilita boas práticas de programação: uso de interfaces no lugar de classes • Não envasivo: o código do sistema depende o mínimo possível da API do Framework 11
  • 23.
    Vantagens de usarInjeção de Dependência • Ajuda a escrever código que é fácil de testar: é só criar objetos e atribuir as propriedades desejadas usando os setters • Facilita boas práticas de programação: uso de interfaces no lugar de classes • Não envasivo: o código do sistema depende o mínimo possível da API do Framework • Dependências são explícitas e evidentes 11
  • 24.
    Vantagens de usarInjeção de Dependência • Ajuda a escrever código que é fácil de testar: é só criar objetos e atribuir as propriedades desejadas usando os setters • Facilita boas práticas de programação: uso de interfaces no lugar de classes • Não envasivo: o código do sistema depende o mínimo possível da API do Framework • Dependências são explícitas e evidentes • Como os componentes não precisam procurar colaboradores em tempo de execução, o código fica mais fácil de escrever e manter 11
  • 25.
    Frameworks para todasas linguagens 12
  • 26.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind 12
  • 27.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer 12
  • 28.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET 12
  • 29.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python 12
  • 30.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python • Ruby - Needle 12
  • 31.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python • Ruby - Needle • Perl - IOC Module 12
  • 32.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python • Ruby - Needle • Perl - IOC Module • Flex - Flicc 12
  • 33.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python • Ruby - Needle • Perl - IOC Module • Flex - Flicc 12
  • 34.
    Frameworks para todasas linguagens • Java - Spring, PicoContainer, HiveMind • PHP - DIContainer • .NET - Spring.NET, PicoContainer.NET • Python - PyContainer, Spring Python • Ruby - Needle • Perl - IOC Module • Flex - Flicc • mais em http://en.wikipedia.org/wiki/Dependency_injection 12
  • 35.
    DI – Linhado Tempo
  • 36.
  • 37.
    Construtor ou Setter? •Construtor com parâmetros deixa claro o que é preciso para criar o objeto
  • 38.
    Construtor ou Setter? •Construtor com parâmetros deixa claro o que é preciso para criar o objeto • Usando construtor evita campos imutáveis de serem alterados
  • 39.
    Construtor ou Setter? •Construtor com parâmetros deixa claro o que é preciso para criar o objeto • Usando construtor evita campos imutáveis de serem alterados • Cuidado: construtores com muitos parâmetros podem ser um indicativo de objeto com responsabilidades demais
  • 40.
    Construtor ou Setter? •Construtor com parâmetros deixa claro o que é preciso para criar o objeto • Usando construtor evita campos imutáveis de serem alterados • Cuidado: construtores com muitos parâmetros podem ser um indicativo de objeto com responsabilidades demais • Construtor é ruim se tiver parâmetros simples como Strings: com setter você cria um método que identifica o que a string significa
  • 41.
    Construtor ou Setter? •Construtor com parâmetros deixa claro o que é preciso para criar o objeto • Usando construtor evita campos imutáveis de serem alterados • Cuidado: construtores com muitos parâmetros podem ser um indicativo de objeto com responsabilidades demais • Construtor é ruim se tiver parâmetros simples como Strings: com setter você cria um método que identifica o que a string significa • Receita geral: comece com construtor e mude para setter se a coisa ficar complicada demais
  • 42.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); for (Phone phone: phones) { Phone phone = it.next(); if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 15
  • 43.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); for (Phone phone: phones) { Phone phone = it.next(); if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 15
  • 44.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); for (Phone phone: phones) { Preencher os dados Phone phone = it.next(); no DB manualmente if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 15
  • 45.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); E se eu não tiver acesso ao banco? for (Phone phone: phones) { Preencher os dados Phone phone = it.next(); no DB manualmente if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 15
  • 46.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); E se eu não tiver acesso ao banco? for (Phone phone: phones) { Preencher os dados Phone phone = it.next(); no DB manualmente E se ele for lento demais? if (!phone.getCompany().equals(name)) { phones.remove(phone); } } return phones; } 15
  • 47.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); E se eu não tiver acesso ao banco? for (Phone phone: phones) { Preencher os dados Phone phone = it.next(); no DB manualmente E se ele for lento demais? if (!phone.getCompany().equals(name)) { E se o custo for phones.remove(phone); alto? } } return phones; } 15
  • 48.
    Voltando ao nossoPhoneLister public List<Phone> phonesForCompany(String name) { Finder List<Phone> phones = finder.findAll(); Iterator<Phone> it = phones.iterator(); E se eu não tiver acesso ao banco? for (Phone phone: phones) { Preencher os dados Phone phone = it.next(); no DB manualmente E se ele for lento demais? if (!phone.getCompany().equals(name)) { E se o custo for phones.remove(phone); alto? } E se não tiver } banco ainda? return phones; } 15
  • 49.
    Testes com Dublês(Doubles) Double 16
  • 50.
    Testes com Dublês(Doubles) Double 16
  • 51.
    Testes com Dublês(Doubles) Double Dummy Object 16
  • 52.
    Testes com Dublês(Doubles) Double Dummy Stub Object 16
  • 53.
    Testes com Dublês(Doubles) Double Dummy Stub Spy Object 16
  • 54.
    Testes com Dublês(Doubles) Double Dummy Stub Spy Mock Object 16
  • 55.
    Testes com Dublês(Doubles) Double Dummy Stub Spy Mock Fake Object 16
  • 56.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros 17
  • 57.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Product product = new Product("Dummy Product"); State state = new State("West Dakota", "WD"); City city = new City("Centreville", state); Address address = new Address("123 Blake St.", city, "12345"); Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 58.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Dummy Product product = new Product("Dummy Product"); State state = new State("West Dakota", "WD"); City city = new City("Centreville", state); Address address = new Address("123 Blake St.", city, "12345"); Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 59.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Dummy Product product = new Product("Dummy Product"); Dummy State state = new State("West Dakota", "WD"); City city = new City("Centreville", state); Address address = new Address("123 Blake St.", city, "12345"); Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 60.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Dummy Product product = new Product("Dummy Product"); Dummy State state = new State("West Dakota", "WD"); Dummy City city = new City("Centreville", state); Address address = new Address("123 Blake St.", city, "12345"); Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 61.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Dummy Product product = new Product("Dummy Product"); Dummy State state = new State("West Dakota", "WD"); Dummy City city = new City("Centreville", state); Dummy Address address = new Address("123 Blake St.", city, "12345"); Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 62.
    Dummy Object • Objetos que nunca são usados, só servem para preencher parâmetros public void testInvoice_addLineItem_noECS() { final int QUANTITY = 1; Dummy Product product = new Product("Dummy Product"); Dummy State state = new State("West Dakota", "WD"); Dummy City city = new City("Centreville", state); Dummy Address address = new Address("123 Blake St.", city, "12345"); Dummy Customer customer= new Customer("Dummy Customer"); Invoice inv = new Invoice(customer); // Add an item to the invoice inv.addItemQuantity(product, QUANTITY); // Verify that item was inserted List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(inv, product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 17
  • 63.
    Stub • Provê alguns dados estáticos que serão usados nos testes • Não funciona para outras coisas além do que está no teste 18
  • 64.
    Stub • Provê alguns dados estáticos que serão usados nos testes • Não funciona para outras coisas além do que está no teste class OrderStateTester { public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); order.fill(new Customer("techday@locaweb.com.br")); assertEquals("techday@locaweb.com.br", order.getEmail()); } } 18
  • 65.
    Spy • Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas 19
  • 66.
    Spy • Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas class OrderStateTester { public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceSpy mailer = new MailServiceSpy(); order.setMailer(mailer); order.fill(new Customer("techday@locaweb.com.br")); assertEquals(1, mailer.numberSent()); } } 19
  • 67.
    Spy • Muito parecido com Stub, mas com a diferença de que “grava” algumas coisas class OrderStateTester { public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceSpy mailer = new MailServiceSpy(); order.setMailer(mailer); order.fill(new Customer("techday@locaweb.com.br")); assertEquals(1, mailer.numberSent()); } } public class MailServiceSpy implements MailService { private List<Message> messages = new ArrayList<Message>(); public void send(Message msg) { messages.add(msg); } public int numberSent() { return messages.size(); } } 19
  • 68.
    O que sãoMock Objects? Years later, Mock objects are still quite controversial, often misused and sometimes misunderstood. “Pintside Thoughts: Mock Objects History” —Tim McKinnon
  • 69.
    O que sãoMock Objects? • Cada pessoa entende de um jeito diferente • Terminologia ambígua • Ficou famoso inicialmente na comunidade JAVA & TDD Comportamento Estado testa interação entre testa o resultado dessas objetos interações
  • 70.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); expect(ftpClient.logout()).andReturn(true); expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); uploader.sendFTPFiles(fileNames); verify(ftpUtils, ftpClient); } }
  • 71.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); Criação dos Mocks FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); expect(ftpClient.logout()).andReturn(true); expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); uploader.sendFTPFiles(fileNames); verify(ftpUtils, ftpClient); } }
  • 72.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); Criação dos Mocks FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); Gravando expect(ftpClient.logout()).andReturn(true); Comportamentos expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); uploader.sendFTPFiles(fileNames); verify(ftpUtils, ftpClient); } }
  • 73.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); Criação dos Mocks FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); Gravando expect(ftpClient.logout()).andReturn(true); Comportamentos expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); Fim gravação List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); uploader.sendFTPFiles(fileNames); verify(ftpUtils, ftpClient); } }
  • 74.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); Criação dos Mocks FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); Gravando expect(ftpClient.logout()).andReturn(true); Comportamentos expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); Fim gravação List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); Execução do método uploader.sendFTPFiles(fileNames); a ser testado verify(ftpUtils, ftpClient); } }
  • 75.
    Código Mock public classFTPUploaderTest { public void shouldUploadOnePhoneConfig() throws Exception { FTPUtils ftpUtils = createMock(FTPUtils.class); FTPUploader uploader = new FTPUploader(ftpUtils); Criação dos Mocks FTPClient ftpClient = createMock(FTPClient.class); expect(ftpUtils.getFTPConnection()).andReturn(ftpClient); ftpUtils.delete(ftpClient, "/", "file-name.cfg"); ftpUtils.ftpUpload(ftpClient, "/polycom/", "/", "file-name.cfg"); Gravando expect(ftpClient.logout()).andReturn(true); Comportamentos expect(ftpClient.isConnected()).andReturn(false); expect(ftpClient.disconnect()); replay(ftpUtils, ftpClient); Fim gravação List<String> fileNames = new ArrayList<String>(); fileNames.add("file-name.cfg"); Execução do método uploader.sendFTPFiles(fileNames); a ser testado verify(ftpUtils, ftpClient); Verificação Final } }
  • 76.
    Fake Object • Têm uma implementação completa, mas simples • Não vai para produção • Exemplo: banco de dados em memória 23
  • 77.
    Fake Object • Têm uma implementação completa, mas simples • Não vai para produção • Exemplo: banco de dados em memória public void testReadWrite_inMemory() throws Exception { // Setup: FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl(); facade.setDao(new InMemoryDatabase()); // Exercise: BigDecimal yyc = facade.createAirport("YYC", "Calgary", "Calgary"); BigDecimal lax = facade.createAirport("LAX", "LAX Intl", "LA"); facade.createFlight(yyc, lax); List flights = facade.getFlightsByOriginAirport(yyc); // Verify: assertEquals("# of flights", 1, flights.size()); Flight flight = (Flight) flights.get(0); assertEquals("origin", yyc, flight.getOrigin().getCode()); } 23
  • 78.
    Fake Object • Têm uma implementação completa, mas simples • Não vai para produção • Exemplo: banco de dados em memória public void testReadWrite_inMemory() throws Exception { // Setup: FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl(); facade.setDao(new InMemoryDatabase()); // Exercise: Fake BigDecimal yyc = facade.createAirport("YYC", "Calgary", "Calgary"); BigDecimal lax = facade.createAirport("LAX", "LAX Intl", "LA"); facade.createFlight(yyc, lax); List flights = facade.getFlightsByOriginAirport(yyc); // Verify: assertEquals("# of flights", 1, flights.size()); Flight flight = (Flight) flights.get(0); assertEquals("origin", yyc, flight.getOrigin().getCode()); } 23
  • 79.
    Implementação de FakeObject public class InMemoryDatabase implements FlightDao{ private List airports = new Vector(); public Airport createAirport(String airportCode, String name, String nearbyCity) throws DataException, InvalidArgumentException { Airport result = new Airport(getNextAirportId(), airportCode, name, createCity(nearbyCity)); airports.add(result); return result; } public Airport getAirportByPrimaryKey(BigDecimal airportId) throws DataException{ for (Airport airport: airports) { if (airport.getId().equals(airportId)) { return airport; } } throw new DataException("Airport not found:"+airportId); } } 24
  • 80.
  • 81.
    Mocks nas Linguagens •Java - EasyMock, JMock, Mockito
  • 82.
    Mocks nas Linguagens •Java - EasyMock, JMock, Mockito • Ruby - Mocha, RSpec?
  • 83.
    Mocks nas Linguagens •Java - EasyMock, JMock, Mockito • Ruby - Mocha, RSpec? • .NET - NMock, Rhino Mocks
  • 84.
    Mocks nas Linguagens • Java - EasyMock, JMock, Mockito • Ruby - Mocha, RSpec? • .NET - NMock, Rhino Mocks • Perl - Test::MockObjects
  • 85.
    Mocks nas Linguagens • Java - EasyMock, JMock, Mockito • Ruby - Mocha, RSpec? • .NET - NMock, Rhino Mocks • Perl - Test::MockObjects • C++ - Google Mocks
  • 86.
    Mocks nas Linguagens • Java - EasyMock, JMock, Mockito • Ruby - Mocha, RSpec? • .NET - NMock, Rhino Mocks • Perl - Test::MockObjects • C++ - Google Mocks • PHP - Simple Test for PHP
  • 87.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object 26
  • 88.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object Preenche parâmetros 26
  • 89.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object Provê Preenche dados para parâmetros os testes 26
  • 90.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object Provê Provê Preenche dados para dados e parâmetros os testes grava 26
  • 91.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object Provê Provê Verifica Preenche dados para dados e comporta- parâmetros os testes grava mentos 26
  • 92.
    Testes com Doubles- Recapitulando Double Dummy Stub Spy Mock Fake Object Provê Provê Verifica Implemen- Preenche dados para dados e comporta- tação parâmetros os testes grava mentos +simples 26
  • 93.
    Quiz Dublês + Injeçãode Dependência =
  • 94.
    Quiz Dublês + Injeçãode Dependência = Testes
  • 95.
    Referências • http://www.springframework.org/documentation • http://martinfowler.com/articles/injection.html • http://martinfowler.com/articles/mocksArentStubs.html • http://xunitpatterns.com/ • Anil Hemrajani, Agile Java Development with Spring, Hibernate and Eclipse
  • 96.
    Para aqueles quebuscam sucesso em projetos e negócios, LOCAWEB oferece soluções inovadoras em Hosted IT Services de alta performance e confiabilidade. Data Center - Av. Juscelino Kubitschek, 1830, 10º andar – Itaim Bibi – São Paulo – SP Escritório SP - Av. Juscelino Kubitschek, 1830, 10º andar – Itaim Bibi – São Paulo – SP | (11) 3544-0400 Escritório RS - Av. Carlos Gomes, 1340, 6º andar – Auxiliadora – Porto Alegre – RS | (51) 4062-0203 0800 726 7770 - Demais Estados