Mockito: Mock private Feldinitialisierung


94

Wie kann ich eine Feldvariable verspotten, die inline initialisiert wird?

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

Hier möchte ich verspotten, person.someMethod()während ich die Test.testMethod()Methode teste , für die ich die Initialisierung einer personVariablen verspotten muss . Irgendeine Ahnung?

Bearbeiten: Ich darf die Personenklasse nicht ändern.


1
Dieser Link könnte für Sie hilfreich sein stackoverflow.com/questions/13645571/…
Popeye

2
Sie sollten Ihren Code umgestalten, damit Sie einen Mock für übergeben können Person. Zu den Optionen gehören das Hinzufügen eines Konstruktors oder eine Setter-Methode.
Tim Biegeleisen

Antworten:


108

Mockito wird mit einer Helferklasse geliefert, um Ihnen den Code der Reflexionskesselplatte zu ersparen:

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

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

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

Update: Leider hat das Mockito-Team beschlossen , die Klasse in Mockito 2 zu entfernen. Sie können also wieder Ihren eigenen Reflection-Boilerplate-Code schreiben, eine andere Bibliothek (z. B. Apache Commons Lang ) verwenden oder einfach die Whitebox- Klasse stehlen (sie ist MIT-lizenziert ).

Update 2: JUnit 5 enthält eigene ReflectionSupport- und AnnotationSupport- Klassen, die möglicherweise nützlich sind und Sie vor dem Abrufen einer weiteren Bibliothek bewahren.


Ich spioniere mein Zielobjekt aus anderen Gründen aus. In diesem Fall kann ich den internen Status nicht festlegen, wenn mein Objekt ausspioniert ist.
Arun

Warum nicht? Ich benutze es mit Spionen. Erstellen Sie eine Personeninstanz. Stub, was auch immer gestubbt werden muss, und dann auf Ihrer Testinstanz festlegen.
Ralf

Warnung: Whitebox ist im internalPaket enthalten und scheint unter Mockito 2.6.2 nicht mehr zu funktionieren.
Nova

Sie können FieldSetter.setField () verwenden. Ich habe unten ein Beispiel dafür gegeben.
Raj Kumar

1
@Ralf Da das Ändern eines Referenznamens in Java immer zu einem Kompilierungsfehler führen sollte.
Joe Coder

67

Ziemlich spät zur Party, aber ich wurde hier geschlagen und bekam Hilfe von einem Freund. Die Sache war, PowerMock nicht zu benutzen. Dies funktioniert mit der neuesten Version von Mockito.

Mockito kommt damit org.mockito.internal.util.reflection.FieldSetter.

Grundsätzlich hilft es Ihnen, private Felder mithilfe von Reflektion zu ändern.

So verwenden Sie es:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

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

Auf diese Weise können Sie ein Scheinobjekt übergeben und später überprüfen.

Hier ist die Referenz.


FieldSetterist in Mockito 2.x nicht mehr verfügbar.
Ralf

4
@Ralf Ich verwende die Version mockito-core-2.15.0. Es ist dort verfügbar. static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/… . Trotzdem Beta.
Raj Kumar

1
@ RajKumar der Class#getDeclaredFieldakzeptiert einzelne Parameter, also müssen die Eltern so aussehen FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);. So habe ich es falsch verstanden und dachte, es wäre eine gute Idee, es in Ihrem Beispiel zu beheben. Danke für Ihre Antwort.
Dapeng Li

1
Die Verwendung einer internen API ist nicht die beste Idee
David

@ RajKumar Schön, aber wie Zimbo Rodger sagte: Ist es eine gute Idee, eine interne API zu verwenden? Warum ist es akut intern? Es ist so nützlich zum Testen.
Willi Mentzel

31

Wenn Sie Spring Test verwenden, versuchen Sie es mit org.springframework.test.util.ReflectionTestUtils

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

1
Toll! Könnte helfen, auf diesen Artikel zu verlinken und möglicherweise die Abhängigkeiten aufzunehmen, die man davon benötigt: baeldung.com/spring-reflection-test-utils
Willi Mentzel

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

28

Ich habe bereits die Lösung für dieses Problem gefunden, die ich hier vergessen habe zu posten.

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

Wichtige Punkte für diese Lösung sind:

  1. Ausführen meiner Testfälle mit PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Weisen Sie Powermock an, sich Test.classauf die Manipulation privater Felder vorzubereiten :@PrepareForTest({ Test.class })

  3. Und schließlich verspotten Sie den Konstruktor für die Personenklasse:

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


7
Ihre Erklärung spricht über die mockStaticFunktion, aber das ist in Ihrem Codebeispiel nicht dargestellt. Sollte das Codebeispiel den mockStaticAufruf haben oder ist dies für Konstruktoren nicht erforderlich?
Shadoninja

10

Der folgende Code kann verwendet werden, um den Mapper im REST-Client-Mock zu initialisieren. Das mapperFeld ist privat und muss während der Einrichtung des Komponententests festgelegt werden.

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

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

5

Mit der Anleitung von @ Jarda können Sie dies definieren, wenn Sie für alle Tests den gleichen Wert für die Variable festlegen müssen:

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

Beachten Sie jedoch, dass die Einstellung privater Werte unterschiedlich sein sollte. Wenn sie privat sind, sind aus irgendeinem Grund.

Beispiel, ich benutze es zum Beispiel, um die Wartezeit eines Schlafes in den Unit-Tests zu ändern. In realen Beispielen möchte ich 10 Sekunden schlafen, aber im Unit-Test bin ich zufrieden, wenn es sofort ist. In Integrationstests sollten Sie den tatsächlichen Wert testen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.