![](/img/trans.png)
[英]NullPointerException when stubbing a test method call with Mockito
[英]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。
角落案例:
如果您使用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)
那么在我的情況下,這是因為錯誤的注釋使用。 我使用 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 版本不匹配。
使用@ExtendWith(MockitoExtension.class)
時,請確保您使用的是 JUnit 5: import org.junit.jupiter.api.Test;
使用@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.