Mockito: simulacro de inicialización de campo privado


95

¿Cómo puedo simular una variable de campo que se inicializa en línea?

class Test {
    private Person person = new Person();
    ...
    public void testMethod() {
        person.someMethod();
        ...
    }
}

Aquí quiero simular person.someMethod()mientras pruebo el Test.testMethod()método para el que necesito simular la inicialización de la personvariable. ¿Cualquier pista?

Editar: no puedo modificar la clase Person.


1
Este enlace puede serle útil stackoverflow.com/questions/13645571/…
Popeye

2
Debes refactorizar tu código para que puedas pasar un simulacro Person. Las opciones incluyen agregar un constructor para hacer esto o agregar un método setter.
Tim Biegeleisen

Respuestas:


110

Mockito viene con una clase de ayuda para ahorrarle un código de placa de caldera de reflexión:

import org.mockito.internal.util.reflection.Whitebox;

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    Whitebox.setInternalState(underTest, "person", mockedPerson);
    // ...
}

Actualización: Desafortunadamente, el equipo de mockito decidió eliminar la clase en Mockito 2. Así que vuelve a escribir su propio código repetitivo de reflexión, usa otra biblioteca (por ejemplo, Apache Commons Lang ) o simplemente roba la clase Whitebox (tiene licencia del MIT ).

Actualización 2: JUnit 5 viene con sus propias clases ReflectionSupport y AnnotationSupport que pueden ser útiles y evitar que tenga que utilizar otra biblioteca.


Estoy espiando mi objeto de destino por otras razones y, en este caso, cuando mi objeto es espía, no puedo establecer el estado interno de esta manera.
Arun

Por qué no? Lo estoy usando con espías. Cree una instancia de Person. Stub lo que necesite stubbing, luego configúrelo en su instancia de prueba.
Ralf

Advertencia: Whitebox está en el internalpaquete y parece que ya no funciona en Mockito 2.6.2.
Nova

Puede utilizar FieldSetter.setField (). He dado un ejemplo a continuación para lo mismo.
Raj Kumar

1
@Ralf Porque cambiar un nombre de referencia en Java siempre debería resultar en un error de compilación.
Joe Coder

69

Bastante tarde para la fiesta, pero me golpearon aquí y recibí ayuda de un amigo. La cosa era no usar PowerMock. Esto funciona con la última versión de Mockito.

Mockito viene con esto org.mockito.internal.util.reflection.FieldSetter .

Básicamente, lo que hace es ayudarlo a modificar los campos privados mediante la reflexión.

Así es como se usa:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
    // ...
    verify(mockedPerson).someMethod();
}

De esta manera puede pasar un objeto simulado y luego verificarlo más tarde.

Aquí está la referencia.


FieldSetterya no está disponible en Mockito 2.x.
Ralf

4
@Ralf Estoy usando la versión mockito-core-2.15.0. Está disponible allí. static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/… . Aunque todavía es beta.
Raj Kumar

1
@RajKumar Class#getDeclaredFieldacepta un solo parámetro, por lo que los parens deben verse así FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);. Así fue como me equivoqué y pensé que podría ser una buena idea arreglarlo en tu ejemplo. Gracias por responder.
Dapeng Li

1
usar API interna no es la mejor idea
David

@RajKumar Bien, pero como mencionó Zimbo Rodger: ¿Es una buena idea utilizar una API interna? ¿Por qué es interna de forma aguda? Es muy útil para probar.
Willi Mentzel

32

En caso de que use Spring Test, pruebe org.springframework.test.util.ReflectionTestUtils

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);

1
¡Excelente! Podría ayudar a vincular a este artículo y tal vez incluir las dependencias que uno necesita de él: baeldung.com/spring-reflection-test-utils
Willi Mentzel

build.gradle.kts:testImplementation("org.springframework:spring-test:5.1.2.RELEASE")
Willi Mentzel

28

Ya encontré la solución a este problema que olvidé publicar aquí.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
    Test test= new Test();
    test.testMethod();
    }
}

Los puntos clave de esta solución son:

  1. Ejecutando mis casos de prueba con PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Instruya a Powermock para que se prepare Test.classpara la manipulación de campos privados:@PrepareForTest({ Test.class })

  3. Y finalmente burlarse del constructor para la clase Person:

    PowerMockito.mockStatic(Person.class); PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);


8
Su explicación habla sobre la mockStaticfunción, pero eso no está representado en su ejemplo de código. ¿Debería el ejemplo de código tener la mockStaticllamada o no es necesario para los constructores?
Shadoninja

10

El siguiente código se puede utilizar para inicializar el asignador en la simulación del cliente REST. El mappercampo es privado y debe configurarse durante la configuración de la prueba unitaria.

import org.mockito.internal.util.reflection.FieldSetter;

new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());

5

Usando la guía de @ Jarda, puede definir esto si necesita configurar la variable con el mismo valor para todas las pruebas:

@Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
    FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}

Pero tenga en cuenta que la configuración de valores privados para que sean diferentes debe manejarse con cuidado. Si son privados lo son por alguna razón.

Por ejemplo, lo uso, por ejemplo, para cambiar el tiempo de espera de un sueño en las pruebas unitarias. En ejemplos reales, quiero dormir durante 10 segundos, pero en la prueba unitaria estoy satisfecho si es inmediato. En las pruebas de integración, debe probar el valor real.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.