Mockito: como verificar o método foi chamado em um object criado dentro de um método?

Eu sou novo no Mockito.

Dada a class abaixo, como posso usar o Mockito para verificar se someMethod foi invocado exatamente uma vez depois que foo foi invocado?

 public class Foo { public void foo(){ Bar bar = new Bar(); bar.someMethod(); } } 

Eu gostaria de fazer a seguinte chamada de verificação:

 verify(bar, times(1)).someMethod(); 

onde bar é uma instância falsa de Bar .

dependency injection

Se você injetar a instância Bar ou uma fábrica usada para criar a instância Bar (ou uma das outras 483 maneiras de fazer isso), você terá o access necessário para executar o teste.

Exemplo de Fábrica:

Dada uma class Foo escrita assim:

 public class Foo { private BarFactory barFactory; public Foo(BarFactory factory) { this.barFactory = factory; } public void foo() { Bar bar = this.barFactory.createBar(); bar.someMethod(); } } 

no seu método de teste você pode injetar um BarFactory assim:

 @Test public void testDoFoo() { Bar bar = mock(Bar.class); BarFactory myFactory = new BarFactory() { public Bar createBar() { return bar;} }; Foo foo = new Foo(myFactory); foo.foo(); verify(bar, times(1)).someMethod(); } 

Bônus: Este é um exemplo de como o TDD pode orientar o design do seu código.

A resposta clássica é “você não faz”. Você testa a API pública do Foo , não seus internos.

Existe algum comportamento do object Foo (ou, menos bom, algum outro object no ambiente) que é afetado por foo() ? Se sim, teste isso. E se não, o que o método faz?

Se você não quiser usar DI ou Fábricas. Você pode refatorar sua aula de uma maneira um pouco complicada:

 public class Foo { private Bar bar; public void foo(Bar bar){ this.bar = (bar != null) ? bar : new Bar(); bar.someMethod(); this.bar = null; // for simulating local scope } } 

E sua aula de teste:

 @RunWith(MockitoJUnitRunner.class) public class FooTest { @Mock Bar barMock; Foo foo; @Test public void testFoo() { foo = new Foo(); foo.foo(barMock); verify(barMock, times(1)).someMethod(); } } 

Então a class que está chamando seu método foo fará assim:

 public class thirdClass { public void someOtherMethod() { Foo myFoo = new Foo(); myFoo.foo(null); } } 

Como você pode ver ao chamar o método dessa maneira, não precisa importar a class Bar em qualquer outra class que esteja chamando seu método foo, que talvez seja algo que você queira.

Claro que a desvantagem é que você está permitindo que o chamador defina o Objeto de Barra.

Espero que ajude.

Solução para o seu código de exemplo usando o PowerMockito.whenNew

  • mockito-all 1.10.8
  • powermock-core 1.6.1
  • powermock-module-junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • jun 4.12

FooTest.java

 package foo; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; //Both @PrepareForTest and @RunWith are needed for `whenNew` to work @RunWith(PowerMockRunner.class) @PrepareForTest({ Foo.class }) public class FooTest { // Class Under Test Foo cut; @Mock Bar barMock; @Before public void setUp() throws Exception { cut = new Foo(); } @After public void tearDown() { cut = null; } @Test public void testFoo() throws Exception { // Setup PowerMockito.whenNew(Bar.class).withNoArguments() .thenReturn(this.barMock); // Test cut.foo(); // Validations Mockito.verify(this.barMock, Mockito.times(1)).someMethod(); } } 

Saída JUnit Saída JUnit

Eu acho que Mockito @InjectMocks é o caminho a percorrer.

Dependendo da sua intenção, você pode usar:

  1. Injeção de construtor
  2. Injeção de incubadora de propriedades
  3. Injeção de campo

Mais informações em docs

Abaixo está um exemplo com injeção de campo:

Classes:

 public class Foo { private Bar bar = new Bar(); public void foo() { bar.someMethod(); } } public class Bar { public void someMethod() { //something } } 

Teste:

 @RunWith(MockitoJUnitRunner.class) public class FooTest { @Mock Bar bar; @InjectMocks Foo foo; @Test public void FooTest() { doNothing().when( bar ).someMethod(); foo.foo(); verify(bar, times(1)).someMethod(); } } 

Sim, se você realmente quiser / precisar fazer isso, você pode usar o PowerMock. Isso deve ser considerado um último recurso. Com o PowerMock, você pode fazer com que ele retorne uma simulação da chamada para o construtor. Então faça a verificação no mock. Dito isto, o csturtz é a resposta “certa”.

Aqui está o link para a construção Mock de novos objects