Mockito: ¿cómo verificar que se llamó al método en un objeto creado dentro de un método?

Resuelto mre asked hace 12 años • 8 respuestas

Soy nuevo en Mockito.

Dada la clase siguiente, ¿cómo puedo usar Mockito para verificar que someMethodse invocó exactamente una vez después foode invocarse?

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

Me gustaría hacer la siguiente llamada de verificación,

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

¿Dónde barhay una instancia simulada de Bar.

mre avatar Mar 23 '12 22:03 mre
Aceptado

Inyección de dependencia

Si inyecta la instancia de Bar, o una fábrica que se utiliza para crear la instancia de Bar (o una de las otras 483 formas de hacerlo), tendrá el acceso necesario para realizar la prueba.

Ejemplo de fábrica:

Dada una clase Foo escrita así:

public class Foo {
  private BarFactory barFactory;

  public Foo(BarFactory factory) {
    this.barFactory = factory;
  }

  public void foo() {
    Bar bar = this.barFactory.createBar();
    bar.someMethod();
  }
}

en tu método de prueba puedes inyectar un BarFactory como este:

@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();
}

Bonificación: este es un ejemplo de cómo TDD (Test Driven Development) puede impulsar el diseño de su código.

csturtz avatar Mar 23 '2012 15:03 csturtz

La respuesta clásica es: "No lo haces". Usted prueba la API pública de Foo, no sus partes internas.

¿Hay algún comportamiento del Fooobjeto (o, menos bueno, de algún otro objeto en el entorno) que se vea afectado por foo()? Si es así, pruébalo. Y si no, ¿para qué sirve el método?

Michael Brewer-Davis avatar Mar 23 '2012 15:03 Michael Brewer-Davis

Creo que Mockito @InjectMockses el camino a seguir.

Dependiendo de tu intención puedes utilizar:

  1. inyección de constructor
  2. Inyección de establecimiento de propiedad
  3. Inyección de campo

Más información en documentos

A continuación se muestra un ejemplo con inyección de campo:

Clases:

public class Foo
{
    private Bar bar = new Bar();

    public void foo() 
    {
        bar.someMethod();
    }
}

public class Bar
{
    public void someMethod()
    {
         //something
    }
}

Prueba:

@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();
    }
}
siulkilulki avatar Apr 11 '2016 06:04 siulkilulki

Si no desea utilizar DI o Fábricas. Puedes refactorizar tu clase de una manera un poco 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
    }
}

Y tu clase de prueba:

@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();
    }
}

Entonces la clase que llama a tu método foo lo hará así:

public class thirdClass {

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

Como puede ver al llamar al método de esta manera, no necesita importar la clase Bar en ninguna otra clase que esté llamando a su método foo, que tal vez sea algo que desee.

Por supuesto, la desventaja es que le permite a la persona que llama configurar el objeto de barra.

Espero eso ayude.

raspacorp avatar Dec 18 '2013 22:12 raspacorp