簡體   English   中英

Mockito - 存根方法時出現 NullpointerException

[英]Mockito - NullpointerException when stubbing Method

所以我開始為我們的 Java-Spring-項目編寫測試。

我使用的是 JUnit 和 Mockito。據說,當我使用 when()...thenReturn() 選項時,我可以模擬服務,而無需模擬它們。 所以我想做的是設置:

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

但無論我執行哪個 when 子句,我總是得到 NullpointerException,這當然是有道理的,因為輸入是 null。

此外,當我嘗試從 object 模擬另一種方法時:

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

在那里我也得到一個 Nullpointer,因為該方法需要一個未設置的變量。

但是我想使用 when()..thenReturn() 來繞過創建這個變量等等。 我只是想確保,如果任何 class 調用此方法,那么無論如何,只需返回 true 或上面的列表。

這基本上是我這邊的誤解,還是有其他問題?

代碼:

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

這是我的測試 class:

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);


}

我遇到了這個問題,我的問題是我使用any()而不是anyInt()調用我的方法。 所以我有:

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

我不得不將其更改為:

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

我不知道為什么會產生 NullPointerException。 也許這會幫助下一個可憐的靈魂。

對於布爾方法,尚未存根的方法的默認返回值為false ,對於返回集合或映射的方法,返回空集合或映射,否則為null

這也適用於when(...)中的方法調用。 在您的示例中when(myService.getListWithData(inputData).get())將導致 NullPointerException 因為myService.getListWithData(inputData)null - 它之前沒有被存根。

一種選擇是為所有中間返回值創建模擬,並在使用前存根它們。 例如:

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

或者,您可以在創建模擬時指定不同的默認答案,以使方法返回新模擬而不是 null: RETURNS_DEEP_STUBS

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

您應該閱讀Mockito.RETURNS_DEEP_STUBS的 Javadoc,它更詳細地解釋了這一點,並且還有一些關於其使用的警告。

我希望這有幫助。 請注意,您的示例代碼似乎有更多問題,例如缺少斷言或驗證語句以及在模擬上調用設置器(這沒有任何效果)。

我遇到了同樣的問題,我的問題只是我沒有使用 @RunWith 正確注釋類。 在您的示例中,請確保您擁有:

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

一旦我這樣做了,NullPointerExceptions 就消失了。

對於未來的讀者來說,使用模擬時 NPE 的另一個原因是忘記初始化模擬,如下所示:

@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");
   // ...
}

還要確保所有注釋都使用 JUnit。 我曾經不小心用來自 testNG 的 @Test 創建了一個測試,所以 @Before 不能使用它(在 testNG 中,注釋是 @BeforeTest)

對我來說,我獲得 NPE 的原因是我在模擬原語時使用Mockito.any() 我發現通過切換到使用來自 mockito 的正確變體可以消除錯誤。

例如,要模擬一個將原始long作為參數的函數,而不是使用any() ,您應該更具體並將其替換為any(Long.class)Mockito.anyLong()

希望對某人有所幫助。

確保你初始化你的模擬。

JUnit4使用@Before

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

JUnit5使用@BeforeEach

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

對於JUnit5檢查,您也在使用正確的導入。

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

@RunWith(MockitoJUnitRunner.class)

由於這是我發現的與我遇到的問題最接近的結果,因此這是出現的第一個結果,但我沒有找到合適的答案,我將在此處為任何未來的可憐人發布解決方案:

any()在模擬類方法使用原始參數的情況下不起作用。

 public Boolean getResult(String identifier, boolean switch)

以上將產生與OP相同的問題。

解決方案,只需包裝它:

 public Boolean getResult(String identifier, Boolean switch)

后者解決了NPE。

  • 請記住,如果您選擇這種方法,現在您可能希望在生產代碼中包含布爾值的空檢查(信用:由 Ridcully 提出)

角落案例:
如果您使用Scala並嘗試在value class上創建any匹配器,您將得到一個無用的 NPE。

所以給定case class ValueClass(value: Int) extends AnyVal ,你想要做的是ValueClass(anyInt)而不是any[ValueClass]

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

另一個SO 問題更具體地說明了這一點,但是當您不知道問題出在值類上時,您會錯過它。

您需要初始化 MockitoAnnotations.initMocks(this) 方法必須調用以初始化帶注釋的字段。

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

有關更多詳細信息,請參閱文檔

對於 JUnit 5,測試類必須使用以下注釋:

@ExtendWith(MockitoExtension.class)

進口:

import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

我的問題已通過此添加解決。

另一個常見的問題是方法簽名被意外聲明為“final”。

這吸引了很多從事代碼庫工作的人,這些代碼庫受到 Checkstyle 的影響,並內化了將成員標記為final的需求。

即在OP的例子中:

object.method()

確保method()未聲明為final

public final Object method() {
}

Mockito 無法模擬最終方法,這將作為一個包裝的 NPE 出現:

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

深埋在錯誤消息中的是以下內容:

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.

對我來說,這是因為我在@BeforeAll方法中存根模擬。

MockitoExtension沒有@BeforeAll的回調。

public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver

我在它工作的測試方法中移動了存根!

就我而言,Intellij 使用org.junit.jupiter.api.Test (Junit5) 而不是 import org.junit.Test of (Junit4) 創建了 Test,這導致所有 bean 顯然都為空。 另外,確保類和測試方法是公開的

@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 {

 }
}

以上所有注釋代碼都不是必需的 { mockContext = Mockito.mock(Context.class); },如果你使用@Mock Annotation to Context mockContext;

@Mock 
Context mockContext; 

但是,如果您僅使用@RunWith(MockitoJUnitRunner.class)它將起作用。 根據 Mockito,您可以使用@Mock 或 Mockito.mock(Context.class);創建模擬對象。 ,

由於使用了@RunWith(PowerMockRunner.class),我得到了 NullpointerException,而不是我改為@RunWith(MockitoJUnitRunner.class) 它工作正常

就我而言,我錯過了 add first

PowerMockito.spy(ClassWhichNeedToBeStaticMocked.class);

所以這對看到此類錯誤的人會有所幫助

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)

以上答案都沒有幫助我。 我一直在努力理解為什么代碼在 Java 中有效,但在 Kotlin 中無效

然后我從這個線程中弄清楚了。

您必須使類和成員函數open ,否則會拋出 NPE。

使功能open后測試開始通過。

您不妨考慮使用編譯器的“全開放”插件

Kotlin默認具有類及其成員 final ,這使得使用 Spring AOP 等要求類是開放的框架和庫很不方便。 all-open編譯器插件使 Kotlin 適應這些框架的要求,並使帶有特定注釋的類及其成員在沒有顯式 open 關鍵字的情況下打開。

那么在我的情況下,這是因為錯誤的注釋使用。 我使用 junit 4 進行測試,並在初始化時使用@BeforeEach而不是@Before

將其更改為@Before ,它就像魅力一樣。

就我而言, when()的導入錯誤。

我偶然使用了import static reactor.core.publisher.Mono.when

Ed Webb 的回答對我有幫助。 相反,您也可以嘗試添加

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

如果你@RunWith(JUnit4.class)

這些答案都不適合我。 這個答案並不能解決 OP 的問題,但由於這篇文章是唯一出現在谷歌搜索這個問題的帖子,所以我在這里分享我的答案。

我在為 Android 編寫單元測試時遇到了這個問題。 問題是我正在測試的活動擴展了AppCompatActivity而不是Activity 為了解決這個問題,我可以將AppCompatActivity替換為Activity ,因為我真的不需要它。 這對每個人來說可能不是一個可行的解決方案,但希望知道根本原因會對某人有所幫助。

使用 JUnit 5 或更高版本時。 您必須在@BeforeEach設置中注入帶有@Mock注釋的類。

在我的情況下,這是由於 @Test 注釋的錯誤導入

確保您使用的是以下導入

import org.junit.jupiter.api.Test;

使用@ExtendWith(MockitoExtension.class)注釋測試類。

當我在 Junit 5 中遇到相同的NullPointerException ,但在我的 maven 項目中正確使用@ExtendWith(MockitoExtension.class)時,這就是谷歌帶我去的地方。

原來我沒有在我的 pom.xml 中包含maven-surefire-plugin ,這意味着@ExtendWith實際上沒有做任何事情!

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.22.1</version>
    </plugin>
    ...

在我的例子中,一個測試方法調用另一個方法作為參數:

Mockito.`when`(repository.getItems(prefs.getUser().id)).thenReturn(listOf())`

在模擬repository時, prefs.getUser().id)將拋出 NPE。 所以,首先我們應該模擬一個參數,例如,

Mockito.`when`(prefs.getUser()).thenReturn(User(id = 1, name = "user"))`

我們也應該模擬prefs 我沒有檢查它並改變了一個圖書館,對不起。

我試圖模擬一個“最終”方法,這顯然是問題所在。

處理此問題的正確方法是使用接口並模擬該接口,但是我無法控制“最終”方法所在的庫。

Mockito 2 可以處理模擬 final 方法。 在項目的 src/test/resources/mockito-extensions 目錄中添加一個名為 org.mockito.plugins.MockMaker 的文本文件並添加一行文本:

mock-maker-inline

之后,模擬最終方法應該可以正常工作。

檢查您使用的 Junit 版本。 在 Maven/Gradle 構建工具中,如果您設置使用testRuntimeOnly 'junit5' ,那么它可能不會使用@RunWith ,因為它不可用並且在 Junit5 中它被替換為@ExtendWith

這不回答 OP 的原始查詢,但在這里嘗試幫助其他人解決 Mockito null 指針異常(NPE)。

我的 NPE 正在發生,因為我沒有將測試依賴項下的 class 明確設置為我模擬的類。 因此,被測 class 無法找到其所需的依賴項,從而創建了 NPE。 我傾向於不模擬正在測試的 class(即使用new關鍵字),以確保我得到我的原生 class 行為進行測試。

由於我無法控制的原因,我仍在使用 Junit 4。 工作示例;

待測類

public class ClassUnderTest {
    private DependantClassOne dependantClassOne; 
    private DependantClassTwo dependantClassTwo;

    // remaining class, including setters
}

測試 Class

@RunWith(MockitoJUnitRunner.class)
public class Test {
    private ClassUnderTest classUnderTest;
    private DependantClassOne dependantClassOne;
    private DependantClassTwo dependantClassTwo;
 
    @Before
    public void setup() {
        dependantClassOne = mock(DependantClassOne.class);
        dependantClassTwo = mock(DependantClassTwo.class);
        classUnderTest = new ClassUnderTest();

        classUnderTest.setDependantClassOne(dependantClassOne); //added to prevent NPE
        classUnderTest.setDependantClassTwo(dependantClassTwo); //added to prevent NPE
    }

    // tests
}

就我而言,我的 Mockito 注釋與 JUnit 版本不匹配。

  1. 使用@ExtendWith(MockitoExtension.class)時,請確保您使用的是 JUnit 5: import org.junit.jupiter.api.Test;

  2. 使用@RunWith(MockitoJUnitRunner.class)時,請確保您使用的是 JUnit 4: import org.junit.Test;

在我的案例中,UnderTest class 有兩種注入:通過構造函數 (TheSecondService) 和通過設置器 (TheFirstService)。

通過 setter 進行的所有注入都是 null。這有助於:

@Mock
private TheFirstService theFirstService;
@Mock
private TheSecondService theSecondService;
@InjectMocks
private TheThirdService underTest;
  
@BeforeEach
public void setup() {
    underTest.setTheFirstService(theFirstService);
}

面臨同樣的問題,對我有用的解決方案:

我沒有模擬服務接口,而是使用 @InjectMocks 來模擬服務實現:

@InjectMocks
private exampleServiceImpl exampleServiceMock;

代替 :

@Mock
private exampleService exampleServiceMock;

就我而言,我在映射器聲明中缺少注釋@mock

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM