¿Cómo decirle a un objeto simulado Mockito que devuelva algo diferente la próxima vez que se llame?

202

Por lo tanto, estoy creando un objeto simulado como una variable estática en el nivel de la clase de esta manera ... En una prueba, quiero Foo.someMethod()devolver un cierto valor, mientras que en otra prueba, quiero que devuelva un valor diferente. El problema que tengo es que parece que necesito reconstruir los simulacros para que esto funcione correctamente. Me gustaría evitar reconstruir los simulacros y usar los mismos objetos en cada prueba.

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

En la segunda prueba, sigo recibiendo 0 como valor cuando se llama a testObj.bar () ... ¿Cuál es la mejor manera de resolver esto? Tenga en cuenta que sé que podría usar un simulacro diferente Fooen cada prueba, sin embargo, tengo que encadenar varias solicitudes mockFoo, lo que significa que tendría que encadenar en cada prueba.

Polaris878
fuente

Respuestas:

43

En primer lugar, no hagas que el simulacro sea estático. Que sea un campo privado. Simplemente ponga su clase de configuración en el @Beforeno @BeforeClass. Se puede ejecutar un montón, pero es barato.

En segundo lugar, la forma en que lo tiene ahora es la forma correcta de obtener un simulacro para devolver algo diferente dependiendo de la prueba.

shoebox639
fuente
439

También puede Stub Llamadas consecutivas (# 10 en 2.8.9 api). En este caso, usaría múltiples llamadas de devolución o una llamada de devolución con múltiples parámetros (varargs).

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}
Tony R
fuente
171
Creo que también puede aprovechar el hecho de que .thenReturn () toma varargs, por lo que el código se puede acortar a: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Justin Muller
10
@JustinMuller: creo que vale la pena responder por separado (en lugar de un comentario)
Brian Agnew
16
Esta respuesta no es lo correcto en este caso. Si aplica este método para devolver 0 y 1, estará bien siempre que ejecute test1y luego test2. Pero puede ser que su entorno de integración continua ejecute las pruebas en el otro orden. O puede ser que desee ejecutarlo test2solo, sin ejecutar test1primero, en cuyo caso fallará. Las pruebas unitarias siempre deben ser independientes entre sí; y nunca debería haber una dependencia entre pruebas individuales, o una dependencia en un orden particular de pruebas. Mientras que encadenando thenReturndeclaraciones ...
Dawood ibn Kareem
44
... tiene sus usos, al igual que el uso de varargs para un solo thenReturn, no es una solución correcta en este caso particular. Me parece que las hordas de votantes de aquí probablemente no han entendido la pregunta.
Dawood ibn Kareem
2
Junit en sí no garantiza el orden de prueba sin@FixMethodOrder
Roger
29

Para todos los que buscan devolver algo y luego otra excepción de lanzamiento de llamada:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));

o

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));
Nagy Attila
fuente
19

O, incluso más limpio:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
Pedro H
fuente
2
Esta debería ser la respuesta.
Ikthiander
14

Para cualquier persona que use spy () y doReturn () en lugar del método when ():

lo que necesita para devolver un objeto diferente en diferentes llamadas es esto:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

Para simulacros clásicos:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

o con una excepción lanzada:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
fl0w
fuente