在有安全驗證的情況下做單元測試Test
版本信息
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.14.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.5.14.RELEASE</version>
<!--實際里面spring-security-web的版本是4.2.7-->
</dependency>
添加依賴
<!--spring-security單元測試-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
<!--spring-security單元測試-->
需求
- 在寫單元測試時,需要模擬某個用戶的登錄狀態
- 在寫單元測試時,需要模擬某個用戶具有某個權限,但又不想改變數據庫
- 編寫單元測試時,需求完整調用某個用戶的登錄
解決需求:
springSecurity提供了相關的組件spring-security-test,可參考官方文檔(https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/reference/htmlsingle/#test-method-withmockuser),該組件提供了相關的注解來來模擬用戶登錄信息或者調用用戶登錄的方法,
- @WithMockUser 模擬用戶,手動指定用戶名和授權
- @WithAnonymousUser 模擬匿名用戶
- @WithUserDetails 模擬用戶,給定用戶名,通過自定義UserDetails來認證
- @WithSecurityContext 通過SecurityContext構造器模擬用戶
例如
@Test
@WithMockUser(username="admin",roles={"USER","ADMIN"})
public void getMessageWithMockUserCustomUser() {
String message = messageService.getMessage();
...
}
模擬了一個名叫admin的用戶,擁有角色"USER","ADMIN"
代碼范例
import com.alibaba.fastjson.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 接口測試+ SpringSecurity的用戶登錄模擬
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@Rollback(true)// 事務自動回滾,默認是true。可以不寫
public class ExampleRestClientTest {
private MockMvc mockMvc; // 模擬MVC對象,通過MockMvcBuilders.webAppContextSetup(this.wac).build()初始化。
@Autowired
private WebApplicationContext wac; // 注入WebApplicationContext
@Before // 在測試開始前初始化工作
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).apply(springSecurity()).build();
}
@Test
@WithUserDetails(value = "admin", userDetailsServiceBeanName = "customUserDetailsService")
public void testQ1() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("param1", "valueaa");
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/secmenu/getUserMenuList")
.contentType(MediaType.APPLICATION_JSON_UTF8).content(JSONObject.toJSONString(map)))
.andExpect(status().is(200))// 模擬向testRest發送get請求
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))// 預期返回值的媒體類型text/plain;charset=UTF-8
.andReturn();// 返回執行請求的結果
}
@Test
public void testFormLoginSuccess() throws Exception {
// 測試登錄成功
mockMvc
.perform(formLogin("/login").user("admin").password("123456"))
.andExpect(authenticated());
}
@Test
public void testFormLoginFail() throws Exception {
// 測試登錄失敗
mockMvc
.perform(formLogin("/login").user("admin").password("invalid"))
.andExpect(unauthenticated());
}
@Test
public void testLogoutFail() throws Exception {
// 測試退出登錄
mockMvc.perform(logout("/logout")).andExpect(unauthenticated());
}
}