java - ejemplos - Mockito: ¿cómo verificar que se haya llamado al método en un objeto creado dentro de un método?
contenedores en java (7)
Creo que Mockito @InjectMocks
es el camino a seguir.
Dependiendo de tu intención puedes usar:
- Inyección de constructor
- Iniciador de propiedades de inyección
- Inyección de campo
Más información en docs
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();
}
}
Soy nuevo en Mockito.
Dada la siguiente clase, ¿cómo puedo usar Mockito para verificar que se invocó someMethod
exactamente una vez después de invocar foo
?
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();
donde bar
es una instancia burlada de Bar
.
La respuesta clásica es: "Tú no". Pruebas la API pública de Foo
, no sus componentes internos.
¿Hay algún comportamiento del objeto Foo
(o, menos bueno, algún otro objeto en el entorno) que esté afectado por foo()
? Si es así, prueba eso. Y si no, ¿qué hace el método?
Otra forma sencilla sería agregar alguna declaración de registro a bar.someMethod () y luego verifique que se puede ver el mensaje cuando se ejecuta la prueba; vea ejemplos aquí: Cómo hacer una declaración JUnit en un mensaje en un registrador
Eso es especialmente útil cuando tu Bar.someMethod () es private
.
Sí, si realmente quieres o necesitas hacerlo, puedes usar PowerMock. Esto debe ser considerado como un último recurso. Con PowerMock puede hacer que devuelva un simulacro de la llamada al constructor. A continuación, haga la verificación en el simulacro. Dicho esto, csturtz es la respuesta "correcta".
Aquí está el enlace a la construcción simulada de nuevos objetos.
Si no quieres usar 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 está llamando 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, el inconveniente es que está permitiendo que la persona que llama establezca el objeto Barra.
Espero eso ayude.
Solución para su código de ejemplo usando PowerMockito.whenNew
- mockito-todo 1.10.8
- powermock-core 1.6.1
- powermock-module-junit4 1.6.1
- powermock-api-mockito 1.6.1
- Junit 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();
}
}
Si inyecta la instancia de la barra, o una fábrica que se utiliza para crear la instancia de la barra (o una de las otras 483 formas de hacerlo), tendrá el acceso necesario para realizar la prueba.
Ejemplo de fábrica:
Dada una clase de 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 su método de prueba puede 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 puede impulsar el diseño de su código.