View on GitHub

spring-boot-in-action

Spring Boot 实战笔记

测试 Web 应用程序

模拟 Spring MVC

要在测试里设置Mock MVC,可以使用 MockMvcBuilders ,该类提供了两个静态方法。

两者的主要区别在于:

我们要用的是 webAppContextSetup() 。

测试执行入口

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ReadingListApplication.class)
@WebAppConfiguration // 开启Web上下文测试
public class MockMvcWebTests {

    @Autowired
    private WebApplicationContext webContext; // 注入 WebApplicationContext

    private MockMvc mockMvc;

    @Before
    public void setupMockMvc() {
        // 设置 MockMvc
        mockMvc = MockMvcBuilders.webAppContextSetup(webContext).build();
    }
}

单元测试代码

MockMvcWebTests 类

测试 Web 安全

Spring Security能让你非常方便地测试安全加固后的Web应用程序。

为了利用这点优势,必须在项目里添加Spring Security的测试模块。

<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-test</artifactId>
	<scope>test</scope>
</dependency>

加载 Spring Security 的测试模块之后,只需在创建 MockMvc 实例时运用 Spring Security的配置器。

springSecurity() 方法返回了一个Mock MVC配置器,为Mock MVC开启了Spring Security 支持。

@Before
public void setupMockMvc() {	// 设置 MockMvc
	mockMvc = MockMvcBuilders.webAppContextSetup(webContext)
		.apply(springSecurity()).build();
}

未经身份验证示例

如果请求未经身份验证,重定向到登录页面:

@Test
public void homePage_unauthenticatedUser() throws Exception {
	mockMvc.perform(get("/"))
        .andExpect(status().is3xxRedirection())
        .andExpect(header().string("Location", "http://localhost/login"));
}

经过身份验证示例

对于经过身份验证的请求,Spring Security提供了两个注解。

@WithMockUser 示例

@WithMockUser 绕过了对 UserDetails 对象的正常查询,用给定的值创建了一 个 UserDetails 对象取而代之。

所以,使用@WithMockUser来虚拟一个用户,这个用户可以是不存在的:

@Test
@WithMockUser(username="craig", password="password", roles="READER")
public void homePage_authenticatedUser1() throws Exception {
	mockMvc.perform(get("/"))
		.andExpect(status().isOk())
		.andExpect(view().name("readingList"))
		.andExpect(model().attribute("books", hasSize(0)))
		.andExpect(model().attribute("amazonID", "Jueee"));
}
@WithUserDetails 示例

使用@WithUserDetails,通过 UserDetailsService.loadUserByUsername 根据用户名加载一个用户。

从而,这个用户必须在运行单元测试之前就存在

@Test
@WithUserDetails(value="craig")		// 使用craig用户
public void homePage_authenticatedUser2() throws Exception {
	mockMvc.perform(get("/"))
		.andExpect(status().isOk())
		.andExpect(view().name("readingList"))
		.andExpect(model().attribute("books", hasSize(0)))
		.andExpect(model().attribute("amazonID", "Jueee"));
}

也必须将 SecurityConfig 类 中的 UserDetailsService.loadUserByUsername 进行独立。

配置了一个 UserDetailsService Bean,它会根据给定的用户名查找 并返回一个 Reader 对象:

@Bean
public UserDetailsService userDetailsService() {
	return new UserDetailsService() {
		@Override
		public UserDetails loadUserByUsername(String username) 
            			throws UsernameNotFoundException {
			UserDetails userDetails = readerRepository.getOne(username);
			if (userDetails != null) {
				return userDetails;
			}
			throw new UsernameNotFoundException("User '" + username + "' not found.");
		}
	};
}

单元测试代码

SpringSecurityTests 类