Mockito - NullpointerException beim Stubben der Methode


75

Also fing ich an, Tests für unser Java-Spring-Projekt zu schreiben.

Was ich benutze, ist JUnit und Mockito. Es heißt, wenn ich die Option when () ... thenReturn () verwende, kann ich Dienste verspotten, ohne sie oder so zu simulieren. Ich möchte also Folgendes festlegen:

when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)  

Aber egal welche when-Klausel ich mache, ich bekomme immer eine NullpointerException, was natürlich Sinn macht, weil die Eingabe null ist.

Auch wenn ich versuche, eine andere Methode von einem Objekt zu verspotten:

when(object.method()).thenReturn(true)

Dort bekomme ich auch einen Nullpointer, da die Methode eine Variable benötigt, die nicht gesetzt ist.

Aber ich möchte when () .. thenReturn () verwenden, um diese Variable zu erstellen und so weiter. Ich möchte nur sicherstellen, dass, wenn eine Klasse diese Methode aufruft, egal was passiert, einfach true oder die obige Liste zurückgegeben wird.

Ist es ein grundsätzliches Missverständnis von meiner Seite oder stimmt etwas anderes nicht?

Code:

public class classIWantToTest implements classIWantToTestFacade{
        @Autowired
        private SomeService myService;

        @Override
        public Optional<OutputData> getInformations(final InputData inputData) {
            final Optional<OutputData> data = myService.getListWithData(inputData);
            if (data.isPresent()) {
                final List<ItemData> allData = data.get().getItemDatas();
                    //do something with the data and allData
                return data;
            }

            return Optional.absent();
        }   
}

Und hier ist meine Testklasse:

public class Test {

    private InputData inputdata;

    private ClassUnderTest classUnderTest;

    final List<ItemData> allData = new ArrayList<ItemData>();

    @Mock
    private DeliveryItemData item1;

    @Mock
    private DeliveryItemData item2;



    @Mock
    private SomeService myService;


    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
        myService = mock(myService.class); 
        classUnderTest.setService(myService);
        item1 = mock(DeliveryItemData.class);
        item2 = mock(DeliveryItemData.class);

    }


    @Test
    public void test_sort() {
        createData();
        when(myService.getListWithData(inputdata).get().getItemDatas());

        when(item1.hasSomething()).thenReturn(true);
        when(item2.hasSomething()).thenReturn(false);

    }

    public void createData() {
        item1.setSomeValue("val");
        item2.setSomeOtherValue("test");

        item2.setSomeValue("val");
        item2.setSomeOtherValue("value");

        allData.add(item1);
        allData.add(item2);


}

3
das Objekt in , wenn () ein Modell von Mockito.mock (), es funktioniert nicht für manuell erstellt reale Objekte erstellt werden muß - nicht sicher , ob das Ihr Problem ist, da ich nicht wirklich bin immer , wo Ihr Problem ist ...
Nadir

1
Bitte geben Sie einen Code ein.
Michael Lloyd Lee mlk

1
Es hört sich so an, als wäre das classIwantToTest.object.get()Ergebnis kein Schein, oder das get()gibt null zurück. Aber bitte posten Sie Ihren Code, zeigen Sie Ihren Test und die Initialisierung Ihrer Mocks.
Wikingersteve

Code hinzugefügt. Musste die Variablennamen ändern, da es von meiner Firma ist;).
user5417542

siehe meine Antwort unten
anand krish

Antworten:


59

Der Standardrückgabewert von Methoden, die Sie noch nicht gestoppt haben, ist falsefür boolesche Methoden, eine leere Sammlung oder Map für Methoden, die Sammlungen oder Maps zurückgeben, und nullandere.

Dies gilt auch für Methodenaufrufe innerhalb when(...). In Sie sind Beispiel when(myService.getListWithData(inputData).get())wird eine Nullpointer verursachen , da myService.getListWithData(inputData)ist null- es vorher wurde nicht gerodet.

Eine Möglichkeit besteht darin, Mocks für alle Zwischenrückgabewerte zu erstellen und diese vor der Verwendung zu stubben. Zum Beispiel:

ListWithData listWithData = mock(ListWithData.class);
when(listWithData.get()).thenReturn(item1);
when(myService.getListWithData()).thenReturn(listWithData);

Alternativ können Sie beim Erstellen eines Modells eine andere Standardantwort angeben, damit Methoden ein neues Modell anstelle von null zurückgeben: RETURNS_DEEP_STUBS

SomeService myService = mock(SomeService.class, Mockito.RETURNS_DEEP_STUBS);
when(myService.getListWithData().get()).thenReturn(item1);

Sie sollten das Javadoc von Mockito.RETURNS_DEEP_STUBS lesen, das dies ausführlicher erklärt und auch einige Warnungen bezüglich seiner Verwendung enthält.

Ich hoffe das hilft. Beachten Sie nur, dass Ihr Beispielcode anscheinend mehr Probleme aufweist, z. B. fehlende Anweisungen zum Bestätigen oder Überprüfen und Aufrufen von Setzern für Mocks (was keine Auswirkungen hat).


Wo finde ich die Spezifikation / Dokumente für den ersten Satz der Antwort? Vielen Dank!
Sturzl


166

Ich hatte dieses Problem und mein Problem war, dass ich meine Methode mit any()anstelle von aufrief anyInt(). So hatte ich:

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())

und ich musste es ändern zu:

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())

Ich habe keine Ahnung, warum das eine NullPointerException erzeugt hat. Vielleicht hilft das der nächsten armen Seele.


48
Das hat bei mir funktioniert. Hinterlasse diesen Kommentar für die nächste arme Seele.
Pavan Kumar

47
Matcher funktionieren nicht für Grundelemente. Wenn das Argument also ein Grundelement ist, benötigen Sie anyChar / Int / Boolean usw.
micah

15
Nicht zuzulassen any(), dass für alle Variablen gearbeitet wird und sich als NPE manifestiert, ist gleichbedeutend mit einem Cyberkriegsangriff auf Entwicklerteams.
Habe am

Der Grund dafür ist, dass any () eine generische Objektreferenz zurückgibt. Dieses Objekt ist nicht in eine Ganzzahl umgewandelt, daher wird der generische Typ als "Ganzzahl" festgelegt. Das Objekt ist jedoch null, sodass das Unboxing nicht auf einen Nullzeiger zugreifen kann. anyInt () stellt eine native Ganzzahl bereit, damit es funktioniert.
Edward

Das Ändern von any () in anyInt () hat es auch für mich gelöst - danke! (Mein Fall beinhaltete Java-Tests und Kotlin-Klassen)
Maggnetix

48

Ich hatte das gleiche Problem und mein Problem war einfach, dass ich die Klasse mit @RunWith nicht richtig kommentiert hatte. Stellen Sie in Ihrem Beispiel sicher, dass Sie:

@RunWith(MockitoJUnitRunner.class)
public class Test {
...

Sobald ich das getan habe, sind die NullPointerExceptions verschwunden.


1
Ich habe den Nullzeiger bcoz von @RunWith (PowerMockRunner.class) erhalten, stattdessen habe ich zu @RunWith (MockitoJUnitRunner.class) gewechselt.
Anand Krish

19

Für zukünftige Leser besteht eine weitere Ursache für NPE bei der Verwendung von Mocks darin, zu vergessen, die Mocks wie folgt zu initialisieren:

@Mock
SomeMock someMock;

@InjectMocks
SomeService someService;

@Before
public void setup(){
    MockitoAnnotations.initMocks(this); //without this you will get NPE
}

@Test
public void someTest(){
    Mockito.when(someMock.someMethod()).thenReturn("some result");
   // ...
}

Stellen Sie außerdem sicher, dass Sie JUnit für alle Anmerkungen verwenden. Ich habe einmal versehentlich einen Test mit @Test von testNG erstellt, sodass @Before nicht damit funktioniert hat (in testNG lautet die Anmerkung @BeforeTest).


1
@BeforeEach hat mir hier geholfen, aber es hängt immer vom Ausführungskontext ab.
Mateusz Gebroski

+1 für "Stellen Sie sicher, dass Sie JUnit für alle Anmerkungen verwenden"! Irgendwie nahm Intellij an, dass ich org.junit.jupiter.api.Teststattdessen verwenden möchte, import org.junit.Testwas dazu führte, dass die Mocks anscheinend null waren.
Sayadi

Ich habe diese Zeile "MockitoAnnotations.initMocks (this);"
Ajay Kedare

In meinem Fall habe ich vergessen, meine Variablen effektiv zu initialisieren. DANKE !!
anonym

15

Für mich war der Grund, warum ich NPE bekam, dass ich Mockito.any()beim Verspotten von Primitiven verwendet habe. Ich fand heraus, dass durch den Wechsel zur Verwendung der richtigen Variante von mockito die Fehler beseitigt werden.

Um beispielsweise eine Funktion zu verspotten, die ein Grundelement longals Parameter verwendet, anstatt sie zu verwenden any(), sollten Sie spezifischer sein und diese durch any(Long.class)oder ersetzen Mockito.anyLong().

Hoffe das hilft jemandem.


VIELEN DANK!!!! Ich habe Fehler erhalten, die keinen Sinn machten, aber die Verwendung von Mockito.anyInt () anstelle von Mockito.any () hat das Problem behoben. Beispielfehler:org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced or misused argument matcher detected... You cannot use argument matchers outside of verification or stubbing.
Aaron

7

Stellen Sie sicher, dass Sie Ihre Mocks initialisieren.

JUnit4 verwenden @Before

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

JUnit5 verwenden @BeforeEach

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
}

Zur JUnit5Überprüfung verwenden Sie auch ordnungsgemäße Importe.

import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)

5

Corner Fall:
Wenn Sie mit Scala und Sie versuchen , einen erstellen anyMatcher auf einem Wert Klasse , werden Sie ein wenig hilfreich NPE bekommen.

Also gegeben case class ValueClass(value: Int) extends AnyVal, was Sie tun möchten, ist ValueClass(anyInt)stattany[ValueClass]

when(mock.someMethod(ValueClass(anyInt))).thenAnswer {
   ...
   val v  = ValueClass(invocation.getArguments()(0).asInstanceOf[Int])
   ...
}

Bei dieser anderen SO-Frage geht es genauer darum, aber Sie würden sie verpassen, wenn Sie nicht wissen, dass das Problem bei Wertklassen liegt.


3

Sie müssen die MockitoAnnotations.initMocks (diese) Methode initialisieren, um mit Anmerkungen versehene Felder zu initialisieren.

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

Weitere Einzelheiten finden Sie unter Doc


Die Verwendung von org.junit.jupiter.api.BeforeEachinsted of @Beforehat mir geholfen.
RAM237

1
@RunWith(MockitoJUnitRunner.class) //(OR) PowerMockRunner.class

@PrepareForTest({UpdateUtil.class,Log.class,SharedPreferences.class,SharedPreferences.Editor.class})
public class InstallationTest extends TestCase{

@Mock
Context mockContext;
@Mock
SharedPreferences mSharedPreferences;
@Mock
SharedPreferences.Editor mSharedPreferenceEdtor;

@Before
public void setUp() throws Exception
{
//        mockContext = Mockito.mock(Context.class);
//        mSharedPreferences = Mockito.mock(SharedPreferences.class);
//        mSharedPreferenceEdtor = Mockito.mock(SharedPreferences.Editor.class);
    when(mockContext.getSharedPreferences(Mockito.anyString(),Mockito.anyInt())).thenReturn(mSharedPreferences);
    when(mSharedPreferences.edit()).thenReturn(mSharedPreferenceEdtor);
    when(mSharedPreferenceEdtor.remove(Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
    when(mSharedPreferenceEdtor.putString(Mockito.anyString(),Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
}

@Test
public void deletePreferencesTest() throws Exception {

 }
}

Alle oben genannten kommentierten Codes sind nicht erforderlich { mockContext = Mockito.mock(Context.class);}, wenn Sie @Mock Annotation to verwendenContext mockContext;

@Mock 
Context mockContext; 

Es funktioniert jedoch nur, wenn Sie @RunWith (MockitoJUnitRunner.class) verwenden . Gemäß Mockito können Sie ein Scheinobjekt erstellen, indem Sie entweder @Mock oder Mockito.mock (Context.class) verwenden. ,

Ich habe eine NullpointerException erhalten, weil ich @RunWith (PowerMockRunner.class) verwendet habe. Stattdessen habe ich zu @RunWith (MockitoJUnitRunner.class) gewechselt. Es funktioniert einwandfrei


0

stand vor dem gleichen Problem, der Lösung, die für mich funktionierte:

Anstatt die Service-Schnittstelle zu verspotten, habe ich @InjectMocks verwendet, um die Service-Implementierung zu verspotten:

@InjectMocks
private exampleServiceImpl exampleServiceMock;

Anstatt von :

@Mock
private exampleService exampleServiceMock;

0

Die Antwort von Ed Webb hat in meinem Fall geholfen. Stattdessen können Sie auch versuchen, hinzuzufügen

  @Rule public Mocks mocks = new Mocks(this);

wenn du @RunWith(JUnit4.class).


0

In meinem Fall habe ich das Hinzufügen zuerst verpasst

PowerMockito.spy(ClassWhichNeedToBeStaticMocked.class);

Dies kann also für jemanden hilfreich sein, der einen solchen Fehler sieht

java.lang.NullPointerException
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:42)
    at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:112)

0

Keine dieser Antworten hat bei mir funktioniert. Diese Antwort löst das Problem von OP nicht, aber da dieser Beitrag der einzige ist, der beim Googeln dieses Problems auftaucht, teile ich meine Antwort hier.

Ich bin auf dieses Problem gestoßen, als ich Unit-Tests für Android geschrieben habe. Das Problem war , dass die Tätigkeit , dass ich verlängert teste AppCompatActivitystatt Activity. Um dies zu beheben, konnte ich nur ersetzen AppCompatActivitymit , Activityda ich es nicht wirklich brauchen. Dies ist möglicherweise nicht für alle eine praktikable Lösung, aber hoffentlich hilft es jemandem, die Grundursache zu kennen.


0

Keine der obigen Antworten hat mir geholfen. Ich hatte Mühe zu verstehen, warum Code in Java funktioniert, aber nicht in Kotlin.

Dann habe ich es aus diesem Thread herausgefunden .

Sie müssen Klassen- und Mitgliedsfunktionen erstellen open, andernfalls wurde NPE ausgelöst.

Nach der openDurchführung begannen Funktionstests zu bestehen.

Sie können auch das "All-Open" -Plugin des Compilers verwenden :

Kotlin hat Klassen und deren Mitglieder standardmäßig endgültig , was es unpraktisch macht, Frameworks und Bibliotheken wie Spring AOP zu verwenden, für die Klassen geöffnet sein müssen . Das all-openCompiler-Plugin passt Kotlin an die Anforderungen dieser Frameworks an und lässt Klassen mit einer bestimmten Annotation versehen und ihre Mitglieder ohne das explizite Schlüsselwort open öffnen.


0

Bei Verwendung von JUnit 5 oder höher. Sie müssen die mit Anmerkungen versehene Klasse @Mock in ein @BeforeEachSetup einfügen.


0

Ein weiteres häufiges Problem ist, dass die Methodensignatur versehentlich als "endgültig" deklariert wird.

Dieser fängt eine Menge Leute auf, die an Codebasen arbeiten, die Checkstyle unterliegen und die Notwendigkeit verinnerlicht haben, Mitglieder als zu markieren final.

dh im Beispiel des OP:

object.method()

Stellen Sie sicher, dass dies method()nicht deklariert ist als final:

public final Object method() {
}

Mockito kann sich nicht über eine endgültige Methode lustig machen und dies wird als verpackte NPE erscheinen:

Suppressed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:

Tief in der Fehlermeldung ist Folgendes vergraben:

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

0

Da dies dem Problem, das ich hatte, am nächsten kommt, ist es das erste Ergebnis, das auftaucht, und ich habe keine angemessene Antwort gefunden. Ich werde hier die Lösung für zukünftige arme Seelen veröffentlichen:

any() funktioniert nicht, wenn die Methode der verspotteten Klasse einen primitiven Parameter verwendet.

 public Boolean getResult(String identifier, boolean switch)

Das Obige erzeugt genau das gleiche Problem wie OP.

Lösung, wickeln Sie es einfach ein:

 public Boolean getResult(String identifier, Boolean switch)

Letzteres löst die NPE.


0

In meinem Fall lag es am falschen Import der @ Test-Annotation

Stellen Sie sicher, dass Sie den folgenden Import verwenden

import org.junit.jupiter.api.Test;
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.