[英]Mockito and Javax annotation @PostConstruct
我正在使用 JUnit Jupiter 為一個類編寫 Java 單元測試,該類具有帶有javax.annotation.PostConstruct
注釋的方法。
我想測試類中的一個方法,但遇到了一個問題,即一旦創建對象,@ @PostConstruct
方法總是被調用(不出所料)。 我創建的用於@PostConstruct
方法的 Mockito 模擬對象沒有提供我需要的結果。
這是創建對象時調用的方法:
@Service
@Slf4j
public class MyService {
@Inject
private MyRepository myRepository;
private Integer myId;
@PostConstruct
public void postInit() {
MyObject myObj = myRepository.findByName("FOO");
myId = myObj.getId();
}
public void myMethod() {
... code to be tested here...
}
}
我的 JUnit 測試類包含以下內容:
@ExtendWith(SpringExtension.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class MyServiceTest {
private static final Integer ID = 0;
@Test
public void myTest() {
// Given.
MyObject myObj = new MyObject();
myObj.setId(ID);
MyRepository myRepository = Mockito.mock(MyRepository.class);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
// Test runs here.
myService.myMethod()
}
}
當我運行單元測試時,我可以看到模擬已通過我的 IDE 實例化。 然而,當調用findByName("FOO")
方法時,它總是返回一個 NULL 對象,即使它應該返回一個正確的MyObject
實例。
因此,單元測試失敗並返回 NullPointerException,因為myObj
為 null。
我在這里做的有什么明顯不正確的>?
在此先感謝您的幫助。
我稍微修改了您的測試,通過Mockito
創建MyService
並使myMethod
返回 id:
@ExtendWith(SpringExtension.class)
class MyServiceTest {
private static final Integer ID = 42;
@InjectMocks
MyService myService;
@Test
public void myTest() {
// Given.
MyObject myObj = new MyObject();
myObj.setId(ID);
MyRepository myRepository = Mockito.mock(MyRepository.class);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
// Test runs here.
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
當我運行這個時,我得到:
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "this.myId" is null
at MyService.myMethod(MyService.java:24)
at MyServiceTest.myTest(MyServiceTest.java:28)
...
很明顯,我們對findByName
的嘲笑沒有奏效。
那是因為myRepository
在我們的測試中只是一個局部變量——沒有什么可以將它注入MyService
。
解決此問題的一種方法是不使用 spring 測試基礎架構,而只是使用普通的 Mockito。 我們使用標准的@Mock/@InjectMocks 注釋來創建我們的模擬和我們正在測試的對象。 這里的缺點是我們必須記住自己調用postInit
:
@ExtendWith(MockitoExtension.class)
class MyServiceTest {
private static final Integer ID = 42;
@Mock
MyRepository myRepository;
@InjectMocks
MyService myService;
@Test
public void myTest() {
// Given.
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
myService.postInit();
// Test runs here.
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
要使用 Spring 調用 postInit,您可以這樣做:(我不確定是否有更慣用的方法來創建模擬 MyRepository 實例)
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {MyService.class})
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
@TestConfiguration
static class Configuration {
@Bean
public MyRepository myRepository() {
MyRepository myRepository = mock(MyRepository.class);
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
return myRepository;
}
}
private static final Integer ID = 42;
@Autowired
MyService myService;
@Test
public void myTest() {
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
避免使用@SpringBootTest
一種方法是在我們的測試配置中創建MyService
實例(我也將其轉換為構造函數注入):
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
@TestConfiguration
static class Configuration {
@Bean
public MyRepository myRepository() {
MyRepository myRepository = mock(MyRepository.class);
MyObject myObj = new MyObject();
myObj.setId(ID);
Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
return myRepository;
}
@Bean
public MyService myService(MyRepository myRepository) {
return new MyService(myRepository);
}
}
private static final Integer ID = 42;
@Autowired
MyService myService;
@Test
public void myTest() {
assertThat(myService.myMethod()).isEqualTo(ID);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.