JMockit 101

JMockit 101

*1. Introdução *

Com este artigo, iniciaremos uma nova série centrada no kit de ferramentas de simulação https://jmockit.github.io [JMockit].

Nesta primeira parte, falaremos sobre o que é o JMockit, suas características e como as simulações são criadas e usadas com ele.

Os artigos posteriores se concentrarão e aprofundarão suas capacidades.

===* 2. JMockit *

====* 2.1 Introdução *

Primeiro, vamos falar sobre o que é o JMockit: uma estrutura Java para zombar de objetos em testes (você pode usá-lo para JUnit e http://testng.org/doc/index.html [TestNG]).

Ele usa as APIs de instrumentação do Java para modificar o bytecode das classes durante o tempo de execução, a fim de alterar dinamicamente seu comportamento. Alguns de seus pontos fortes são sua expressibilidade e sua capacidade imediata de zombar de métodos estáticos e privados.

Talvez você seja novo no JMockit, mas definitivamente não é por ser novo. O desenvolvimento do JMockit começou em junho de 2006 e seu primeiro lançamento estável data de dezembro de 2012, então já existe há algum tempo (a versão atual é de 1,24 no momento da redação do artigo).

====* 2.2 A expressibilidade do JMockit *

Como dito anteriormente, um dos pontos mais fortes do JMockit é sua expressibilidade. Para criar zombarias e definir seu comportamento, em vez de chamar métodos da API de zombaria, basta defini-los diretamente.

Isso significa que você não fará coisas como:

API.expect(mockInstance.method()).andThenReturn(value).times(2);

Em vez disso, espere coisas como:

new Expectation() {
    mockInstance.method();
    result = value;
    times = 2;
}

Pode parecer que é mais código, mas você pode simplesmente colocar as três linhas em uma. A parte realmente importante é que você não acaba com um grande "trem" de chamadas de método em cadeia. Em vez disso, você termina com uma definição de como deseja que o mock se comporte quando chamado.

Se você levar em conta que na parte result = value você poderia retornar qualquer coisa (valores fixos, valores gerados dinamicamente, exceções etc.), a expressividade do JMockit fica ainda mais evidente.

====* 2.3 O modelo Record-Replay-Verify *

Os testes usando o JMockit são divididos em três estágios diferenciados: gravar, reproduzir e verificar.

  1. Na fase* record *, durante a preparação do teste e antes das invocações para os métodos que queremos que sejam executados, definiremos o comportamento esperado para todos os testes a serem usados ​​no próximo estágio.

  2. A fase replay é aquela na qual o código em teste é executado. As invocações de métodos/construtores simulados anteriormente gravadas no estágio anterior agora serão repetidas.

  3. Por fim, na fase verificar , afirmaremos que o resultado do teste foi o esperado (e que as zombarias se comportaram e foram usadas de acordo com o que foi definido na fase de registro).

Com um exemplo de código, um wireframe para um teste seria algo como isto:

@Test
public void testWireframe() {
  //preparation code not specific to JMockit, if any

   new Expectations() {{
      //define expected behaviour for mocks
   }};

  //execute code-under-test

   new Verifications() {{
      //verify mocks
   }};

  //assertions
}

*3. Criando zombarias *

====* 3.1 Anotações de JMockit *

Ao usar o JMockit, a maneira mais fácil de usar zombarias, é usar anotações. Existem três para criar zombarias (_ @ Mocked_, _ @ Injectable_ e _ @ Capturing_) e uma para especificar a classe em teste (_ @ Tested_).

Ao usar a anotação _ @ Mocked_ em um campo, ele criará instâncias simuladas de cada novo objeto dessa classe específica.

Por outro lado, com a anotação _ @ Injectable_, apenas uma instância simulada será criada.

A última anotação, _ @ Capturing_, se comportará como _ @ Mocked, _, mas estenderá seu alcance a todas as subclasses que estendem ou implementam o tipo de campo anotado.

====* 3.2 Passando argumentos para testes *

Ao usar o JMockit, é possível passar simulações como parâmetros de teste. Isso é bastante útil para criar uma simulação apenas para esse teste em particular, como um objeto de modelo complexo que precisa de um comportamento específico apenas para um teste, por exemplo. Seria algo como isto:

@RunWith(JMockit.class)
public class TestPassingArguments {

   @Injectable
   private Foo mockForEveryTest;

   @Tested
   private Bar bar;

   @Test
   public void testExample(@Mocked Xyz mockForJustThisTest) {
       new Expectations() {{
           mockForEveryTest.someMethod("foo");
           mockForJustThisTest.someOtherMethod();
       }};

       bar.codeUnderTest();
   }
}

Essa maneira de criar uma simulação passando-a como parâmetro, em vez de precisar chamar algum método de API, mostra novamente a expressibilidade de que estamos falando desde o início.

====* 3.3 Exemplo completo *

Para finalizar este artigo, incluiremos um exemplo completo de um teste usando o JMockit.

Neste exemplo, testaremos uma classe Performer que usa Collaborator em seu método perform () _. Esse método _perform () _ recebe um objeto _Model como um parâmetro do qual ele usará seu getInfo () _ que retorna uma String, essa String será passada para o método _collaborate () _ do _Collaborator que retornará true para esse particular test, e esse valor será passado para o método receive () _ de _Collaborator.

Portanto, as classes testadas ficarão assim:

public class Model {
    public String getInfo(){
        return "info";
    }
}

public class Collaborator {
    public boolean collaborate(String string){
        return false;
    }
    public void receive(boolean bool){
       //NOOP
    }
}

public class Performer {
    private Collaborator collaborator;

    public void perform(Model model) {
        boolean value = collaborator.collaborate(model.getInfo());
        collaborator.receive(value);
    }
}

E o código do teste terminará sendo:

@RunWith(JMockit.class)
public class PerformerTest {

    @Injectable
    private Collaborator collaborator;

    @Tested
    private Performer performer;

    @Test
    public void testThePerformMethod(@Mocked Model model) {
        new Expectations() {{
            model.getInfo();result = "bar";
            collaborator.collaborate("bar"); result = true;
        }};
        performer.perform(model);
        new Verifications() {{
            collaborator.receive(true);
        }};
    }
}

===* 4. Conclusão *

Com isso, encerraremos nossa introdução prática ao JMockit. Se você quiser saber mais sobre o JMockit, fique atento para artigos futuros.

A implementação completa deste tutorial pode ser encontrada em o projeto GitHub.

====* 4.1 Artigos da série *

Todos os artigos da série:

  • link:/jmockit-101 [JMockit 101]

  • link:/jmockit-expectations [Um guia para o JMockit - Expectativas]

  • link:/jmockit-advanced-use [Uso avançado do JMockit]