試題管理系統的安全模塊使用Spring Security
,代碼從原華軟倉庫移植,在移植的過程當中,發現原測試編寫的很差,遂在新系統中對安全模塊測試進行了重構。html
添加@SpringBootTest
註解,意爲這是一個基於SpringBoot
的單元測試。java
SpringBoot
在官方的Guide
中提供了多種測試方式。web
@SpringBootTest
註解中的webEnvironment
屬性能夠配置測試環境,默認爲MOCK
環境。spring
/** * 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
進行攔截,因此測試以前須要進行用戶登陸操做。api
以前都是使用比較笨重的方法,寫一個@Before
,@Before
裏進行登陸,以後再執行測試方法。瀏覽器
最近在閱讀Spring Security Test
文檔以後,終於找到一種模擬登陸十分簡便的方法,@WithMockUser
。安全
test method with mock user - spring security test服務器
引入Spring Security Test
依賴:cookie
<!-- 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();
兩個各有優勢,以前咱們一直使用簡單方便的Mock
環境進行測試,但當咱們有一天,發現這個Mock
環境測試下的MockHttpSession
沒法知足需求的時候,咱們開始探索其餘的測試方案。
真正的掌握,不是知道何時用,而是知道何時不用。