Mockito: finta inizializzazione del campo privato


95

Come posso deridere una variabile di campo che viene inizializzata in linea?

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

Qui voglio deridere person.someMethod()durante il test del Test.testMethod()metodo per il quale ho bisogno di simulare l'inizializzazione della personvariabile. Qualche indizio?

Modifica: non sono autorizzato a modificare la classe Persona.


1
Questo link potrebbe esserti utile stackoverflow.com/questions/13645571/…
Popeye

2
Dovresti eseguire il refactoring del codice in modo da poter passare in un mock per Person. Le opzioni includono l'aggiunta di un costruttore per eseguire questa operazione o l'aggiunta di un metodo setter.
Tim Biegeleisen

Risposte:


110

Mockito viene fornito con una classe di supporto per risparmiare un po 'di codice della piastra della caldaia di riflessione:

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

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

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

Aggiornamento: Sfortunatamente il team di mockito ha deciso di rimuovere la classe in Mockito 2. Quindi sei tornato a scrivere il tuo codice boilerplate di riflessione, usa un'altra libreria (ad esempio Apache Commons Lang ), o semplicemente ruba la classe Whitebox (è con licenza MIT ).

Aggiornamento 2: JUnit 5 viene fornito con le proprie classi ReflectionSupport e AnnotationSupport che potrebbero essere utili e impedirti di inserire un'altra libreria.


Sto spiando il mio oggetto di destinazione per altri motivi e in questo caso quando il mio oggetto è spia, non posso impostare lo stato interno in questo modo.
Arun

Perchè no? Lo sto usando con le spie. Crea un'istanza Person. Stub tutto ciò che necessita di stubbing, quindi impostalo sulla tua istanza di test.
Ralf

Attenzione: Whitebox è nel internalpacchetto e sembra non funzionare più su Mockito 2.6.2.
Nova

È possibile utilizzare FieldSetter.setField (). Ho fornito un esempio di seguito per lo stesso.
Raj Kumar

1
@Ralf Perché la modifica di un nome di riferimento in Java dovrebbe sempre comportare un errore di compilazione.
Joe Coder

69

Piuttosto tardi per la festa, ma qui sono rimasto colpito e ho ricevuto l'aiuto di un amico. Il problema era non usare PowerMock. Funziona con l'ultima versione di Mockito.

Mockito viene fornito con questo org.mockito.internal.util.reflection.FieldSetter.

Ciò che fondamentalmente fa è aiutarti a modificare i campi privati ​​usando la riflessione.

Ecco come lo usi:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

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

In questo modo puoi passare un oggetto fittizio e verificarlo successivamente.

Ecco il riferimento.


FieldSetternon è più disponibile in Mockito 2.x.
Ralf

4
@Ralf Sto usando la versione mockito-core-2.15.0. È disponibile lì. static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/… . Comunque ancora beta.
Raj Kumar

1
@RajKumar Class#getDeclaredFieldaccetta un singolo parametro, quindi i genitori devono assomigliare a questo FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);. È così che ho sbagliato e ho pensato che potrebbe essere una buona idea risolverlo nel tuo esempio. Grazie per la risposta.
Dapeng Li,

1
l'utilizzo dell'API interna non è l'idea migliore
David

@RajKumar Bello, ma come ha detto Zimbo Rodger: è una buona idea usare un'API interna? Perché è interno acutalmente? È così utile per i test.
Willi Mentzel

32

Nel caso in cui utilizzi Spring Test, prova org.springframework.test.util.ReflectionTestUtils

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

1
Grande! Potrebbe aiutare a collegarsi a questo articolo e magari includere le dipendenze di cui si ha bisogno da esso: baeldung.com/spring-reflection-test-utils
Willi Mentzel

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

28

Ho già trovato la soluzione a questo problema che ho dimenticato di postare qui.

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

I punti chiave di questa soluzione sono:

  1. Esecuzione dei miei casi di test con PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Istruire Powermock a prepararsi Test.classper la manipolazione di campi privati:@PrepareForTest({ Test.class })

  3. E infine deridere il costruttore per la classe Person:

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


8
La tua spiegazione parla della mockStaticfunzione, ma non è rappresentata nel tuo esempio di codice. L'esempio di codice dovrebbe avere la mockStaticchiamata o non è richiesto per i costruttori?
Shadoninja

10

Il codice seguente può essere utilizzato per inizializzare il mapper nel mock del client REST. Il mappercampo è privato e deve essere impostato durante la configurazione dello unit test.

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

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

5

Usando la guida di @ Jarda puoi definirlo se devi impostare la variabile lo stesso valore per tutti i test:

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

Ma attenzione che l'impostazione di valori privati ​​diversi dovrebbe essere gestita con cura. Se sono privati ​​lo sono per qualche motivo.

Ad esempio, lo uso, ad esempio, per modificare il tempo di attesa di un sonno negli unit test. In esempi reali vorrei dormire 10 secondi ma in unit-test sono soddisfatto se è immediato. Nei test di integrazione dovresti testare il valore reale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.