引言
試題管理系統的安全模塊使用Spring Security,代碼從原華軟倉庫移植,在移植的過程中,發現原測試編寫的不好,遂在新系統中對安全模塊測試進行了重構。
Spring 測試
添加@SpringBootTest注解,意為這是一個基于SpringBoot的單元測試。
SpringBoot在官方的Guide中提供了多種測試方式。
@SpringBootTest注解中的webEnvironment屬性可以配置測試環境,默認為MOCK環境。
/** * The type of web environment to create when applicable. Defaults to * {@link WebEnvironment#MOCK}. * @return the type of web environment */WebEnvironment webEnvironment() default WebEnvironment.MOCK;
模擬環境測試
啟用Spring Security后,單元測試中對api的測試會被Spring Security的Filter進行攔截,所以測試之前需要進行用戶登錄操作。
之前都是使用比較笨重的方法,寫一個@Before,@Before里進行登錄,之后再執行測試方法。
最近在閱讀Spring Security Test文檔之后,終于找到一種模擬登錄十分簡便的方法,@WithMockUser。
test method with mock user - spring security test
引入Spring Security Test依賴:
<!-- Spring Security Test --><dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope></dependency>
示例代碼如下:
@SpringBootTest@RunWith(SpringRunner.class)@AutoConfigureMockMvc@WithMockUser(username = "admin", password = "admin")public class ControllerTest { @Autowired protected MockMvc mockMvc; @Test void contextLoads() { }}
注:@RunWith(SpringRunner.class)表示當前測試使用org.springframework.test.context.junit4.SpringRunner類來執行,最新的SpringBoot版本中已經全面啟用junit5,不推薦使用junit4.SpringRunner,因為未經過內部學習與測試,未在生產項目中使用。
真實環境測試
為了減少學習與溝通的成本,之前,所有的測試規定都在MOCK環境下,使用MockMVC進行api測試。
雖然MOCK環境能解決大部分的問題,并且可以在不啟動Server的情況下直接進行測試,但在某些場景下,仍需要真實環境下的HTTP服務與請求測試。
啟用Spring Security后,MockMVC是直接測試控制器,并非在真實的HTTP服務器下進行測試,MOCK環境中使用的是MockHttpSession,這不是標準的Session實現,沒有加入對COOKIE的支持,所以在測試安全模塊時,無法像瀏覽器一樣測試COOKIE等認證信息。
spring mockmvc doesn't contain cookies - stackoverflow
去StackOverflow上也沒有解決方案,答案推薦使用TestRestTemplate+真實的服務器環境進行單元測試。
將webEnvironment配置為SpringBootTest.WebEnvironment.RANDOM_PORT,即表示當前測試在一個隨機端口的真實Web環境下運行。
@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)class AuthControllerTest { @Autowired private TestRestTemplate restTemplate;}
測試時使用TestRestTemplate進行網絡請求的發送,真實模擬Web服務器環境。
示例代碼如下:
logger.debug("3: 測試用戶名密碼正確");username = RIGHT_USERNAME;password = RIGHT_PASSWORD;response = this.restTemplate .withBasicAuth(username, password) .getForEntity(CONFIG_LOGIN, Void.class);logger.debug("斷言: 狀態碼為200");assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.OK.value());logger.debug("獲取 response 的 Set-Cookie 信息,并斷言");String setCookie = response.getHeaders().getFirst(HttpHeaders.SET_COOKIE);assertThat(setCookie).isNotNull();
新聞熱點
疑難解答