我正在尝试对Spring引导控制器进行单元测试,但我的一个@Autowired
字段返回null。
我在这个控制器中有两个自动连接的字段:
public class UserProfileController{
@Autowired
private UserProfileService profileService;
@Autowired
private IDataValidator dataValidatorImpl;
我的测试课程如下:
@RunWith(SpringJUnit4ClassRunner.class)
@WebIntegrationTest
@SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
@Mock
UserProfileService profileServiceMock;
@Autowired
ApplicationContext actx;
@InjectMocks
private UserProfileController profileController;
@Before
public void setup() {
// Process mock annotations
String[] asdf = actx.getBeanDefinitionNames();
for (int i = 0; i < asdf.length; i++){
System.out.println(asdf[i]);
}
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(profileController).build();
}
/**
* All this does is verify that we return the correct datatype and HTTP status
* @throws Exception
*/
@Test
public void testGetProfileSuccess() throws Exception {
Mockito.when(profileServiceMock.getProfile(Mockito.any(HashMap.class))).thenReturn(new HashMap<String, Object>());
mockMvc.perform(get("http://localhost:8095/UserName?tenantId=tenant1"))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8));
//verify profileService was only used once
Mockito.verify(profileServiceMock, Mockito.times(1)).getProfile(Mockito.any(HashMap.class));
//verify we're done interacting with profile service
Mockito.verifyNoMoreInteractions(profileServiceMock);
}
如果我在测试类中保持IDataValidator不变,它将为null,并且我得到一个NPE。如果我@Spy
DataValidatorImpl,它就无法从Spring环境中找到它需要工作的属性。
我怎么能让IDataValidator自动连接自己并维护其spring环境上下文,就好像我只是在正常运行应用程序一样?
当我在@Before
setup()方法中打印所有bean时,我可以在列表中看到DataValidationImpl。
当你用模拟你的控制器时
MockMvcBuilders.standaloneSetup(profileController).build();
控制器在上下文中被替换。由于您没有在其中注入任何IDataValidator
,因此它为null。
最简单的解决方案是将实际的IDataValidator
自动连接到测试类中,并将其注入控制器中。
在您的控制器中:
public class UserProfileController{
private UserProfileService profileService;
private IDataValidator dataValidatorImpl;
@Autowired
public UserProfileController(UserProfileService profileService, IDataValidator dataValidatorImpl) {
this.profileService = profileService;
this.dataValidatorImpl = dataValidatorImpl;
}
在你的测试中:
@RunWith(SpringJUnit4ClassRunner.class)
@WebIntegrationTest
@SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
private UserProfileService profileService;
@Autowired
private IDataValidator dataValidator;
@Before
public void setup() {
UserProfileService profileService = Mockito.mock(UserProfileService.class);
UserProfileController controller = new UserProfileController(profileService, dataValidator);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
}
如果我理解正确,您希望为UserProfileController
注入真正的Validator和mock Service。
在这种情况下,我建议使用@ContextConfiguration
annotaion,它允许在测试中配置上下文。您需要创建一个配置类:
@RunWith(SpringJUnit4ClassRunner.class)
@WebIntegrationTest
@SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
@Mock
UserProfileService profileServiceMock;
@Autowired
ApplicationContext actx;
//comment this line out
//@InjectMocks
@Autowired
private UserProfileController profileController;
@Before
public void setup() {
// Process mock annotations
String[] asdf = actx.getBeanDefinitionNames();
for (int i = 0; i < asdf.length; i++){
System.out.println(asdf[i]);
}
//comment this line out
//MockitoAnnotations.initMocks(this);
@Configuration
public static class Config {
//wire validator - if it is not wired by other configurations
@Bean
Validator validator() {
return new Validaor();
}
//wire mock service
@Bean
public UserProfileService profileService() {
return mock(UserProfileService.class);
}
}
好吧,我发誓我第一次这么做了,但当试图为jny重新创建抛出的错误时,它确实起了作用。
我的解决方案是通过@Spy
注释进行注入,并在我的@Before
设置方法中从ApplicationContext
获取bean。
public class ControllerTest {
private MockMvc mockMvc;
@Mock
UserProfileService profileServiceMock;
@Spy
IDataValidator dataValidator;
@Autowired
ApplicationContext actx;
@InjectMocks
private UserProfileController profileController;
@Before
public void setup() {
dataValidator = (IDataValidator) actx.getBean("dataValidatorImpl");
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(profileController).build();
}