(上下文)使用 JUnit4 和 Spring 测试 @RestController 的问题
(Context) Problems with @RestController testing with JUnit4 and Spring
我有 3 个独立的项目,受 pom.xml
约束
主 myapp
文件夹 pom.xml 有模块部分:
<modules>
<module>myapp-dao</module>
<module>myapp-webapp</module>
<module>myapp-configuration</module>
</modules>
myapp
文件夹中的 3 个项目(每个项目都有自己的 pom.xml)
myapp-dao
myapp-configuration
myapp-webapp
我正在为 myapp-webapp
模块中的 @RestController
class 编写一个 JUnit 测试(不用担心 @Test 的内容,它只是一个骨架,它将扩充的时候我就可以运行测试了):
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@WebMvcTest(ContentController.class)
public class ContentControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private ContentService contentService;
@MockBean
private HyperlinkReferenceService hyperlinkReferenceService;
@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
given(contentService.findContentUsedAsTemplateIn(1, 0)).willReturn(null);
mvc.perform(get("/portal/content/1/references/usedAsTemplateIn").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
当我尝试 运行 测试时,我得到:
java.lang.IllegalStateException: Failed to load ApplicationContext
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.pro4people.msg.ServletInitializer]; nested exception is java.io.FileNotFoundException: class path resource [myapp.properties] cannot be opened because it does not exist
我已经通过将 myapp-configuration/target/myapp.properties
复制到 myapp-webapp/src/main/test/resources/myapp.properties
解决了这个问题
这样解决了上面的问题,但是又出现了一个问题:
java.lang.IllegalStateException: Failed to load ApplicationContext
Description:
Field userRepository in com.company.myapp.service.UserServiceImpl required a bean of type 'com.company.myapp.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.company.myapp.repository.UserRepository' in your configuration.
但实际上,我什至没有在此测试中使用 UserRepository,因此我推测周围某处存在 Spring 上下文问题。该应用程序通常构建为 war
并部署到 Tomcat,只有在其 wared
状态下,所有内容才会正确注入和绑定。
这个问题怎么能省略呢?无论我想做什么,测试都会提升 spring 上下文,当我删除 @WebMvcTest(ContentController.class)
时,我得到:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.company.myapp.controller.portal.json.ContentControllerTest': Unsatisfied dependency expressed through field 'mvc'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
这似乎是显而易见的。
在控制器测试的基础上使用上下文配置 class。
@ContextConfiguration(classes = {ContentController.class})
Below code works for unit test for controller.
@RunWith(MockitoJUnitRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {ContentController.class})
public class ContentController {
private MockMvc mockMvc;
@InjectMocks
private ContentController contentController;
@Mock
private ContentService contentService;
....
/**
* Configure the mockMvc with Controller.
*/
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(contentController).build();
}
@Test
your test here() {
//use Mockito.when(statement).thenReturn(value);
//rest remain same using mockMVC
mockMvc.perform(...)
}
我有 3 个独立的项目,受 pom.xml
约束主 myapp
文件夹 pom.xml 有模块部分:
<modules>
<module>myapp-dao</module>
<module>myapp-webapp</module>
<module>myapp-configuration</module>
</modules>
myapp
文件夹中的 3 个项目(每个项目都有自己的 pom.xml)
myapp-dao
myapp-configuration
myapp-webapp
我正在为 myapp-webapp
模块中的 @RestController
class 编写一个 JUnit 测试(不用担心 @Test 的内容,它只是一个骨架,它将扩充的时候我就可以运行测试了):
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@WebMvcTest(ContentController.class)
public class ContentControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private ContentService contentService;
@MockBean
private HyperlinkReferenceService hyperlinkReferenceService;
@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
given(contentService.findContentUsedAsTemplateIn(1, 0)).willReturn(null);
mvc.perform(get("/portal/content/1/references/usedAsTemplateIn").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
当我尝试 运行 测试时,我得到:
java.lang.IllegalStateException: Failed to load ApplicationContext
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.pro4people.msg.ServletInitializer]; nested exception is java.io.FileNotFoundException: class path resource [myapp.properties] cannot be opened because it does not exist
我已经通过将 myapp-configuration/target/myapp.properties
复制到 myapp-webapp/src/main/test/resources/myapp.properties
这样解决了上面的问题,但是又出现了一个问题:
java.lang.IllegalStateException: Failed to load ApplicationContext
Description:
Field userRepository in com.company.myapp.service.UserServiceImpl required a bean of type 'com.company.myapp.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.company.myapp.repository.UserRepository' in your configuration.
但实际上,我什至没有在此测试中使用 UserRepository,因此我推测周围某处存在 Spring 上下文问题。该应用程序通常构建为 war
并部署到 Tomcat,只有在其 wared
状态下,所有内容才会正确注入和绑定。
这个问题怎么能省略呢?无论我想做什么,测试都会提升 spring 上下文,当我删除 @WebMvcTest(ContentController.class)
时,我得到:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.company.myapp.controller.portal.json.ContentControllerTest': Unsatisfied dependency expressed through field 'mvc'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
这似乎是显而易见的。
在控制器测试的基础上使用上下文配置 class。 @ContextConfiguration(classes = {ContentController.class})
Below code works for unit test for controller.
@RunWith(MockitoJUnitRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {ContentController.class})
public class ContentController {
private MockMvc mockMvc;
@InjectMocks
private ContentController contentController;
@Mock
private ContentService contentService;
....
/**
* Configure the mockMvc with Controller.
*/
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(contentController).build();
}
@Test
your test here() {
//use Mockito.when(statement).thenReturn(value);
//rest remain same using mockMVC
mockMvc.perform(...)
}