若是但願很方便針對API進行測試,而且方便的集成到CI中驗證每次的提交,那麼spring boot自帶的IT絕對是不二選擇。前端
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles({Profiles.ENV_IT}) public class DemoIntegrationTest { @Autowired private FooService fooService; @Test public void test() { System.out.println("tested"); } }
其中SpringBootTest
定義了跑IT時的一些配置,上述代碼是用了隨機端口,固然也能夠預約義端口,像這樣java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port=9990"})
ActiveProfiles
強制使用了IT的Profile,從最佳實踐上來講IT Profile所配置的數據庫或者其餘資源組件的地址,應該是與開發或者Staging環境隔離的。由於當一個IT跑完以後不少狀況下咱們須要清除測試數據。git
你可以發現這樣的Case可使用Autowired
注入任何想要的Service。這是由於spring將整個上下文都加載了起來,與實際運行的環境是同樣的,包含了數據庫,緩存等等組件。若是以爲測試時不須要所有的資源,那麼在profile刪除對應的配置就能夠了。這就是一個完整的運行環境,惟一的區別是當用例跑完會自動shutdown。github
強烈推薦一個庫,加入到gradle中web
testCompile 'io.rest-assured:rest-assured:3.0.3'
支持JsonPath,十分好用,具體文檔戳這裏spring
@Sql(scripts = "/testdata/users.sql") @Test public void test001Login() { String username = "demo@demo.com"; String password = "demo"; JwtAuthenticationRequest request = new JwtAuthenticationRequest(username, password); Response response = given().contentType(ContentType.JSON).body(request) .when().post("/auth/login").then() .statusCode(HttpStatus.OK.value()) .extract() .response(); assertThat(response.path("token"), is(IsNull.notNullValue())); assertThat(response.path("expiration"), is(IsNull.notNullValue())); }
@Sql
用於在測試前執行sql插入測試數據。注意given().body()
中傳入的是一個java對象JwtAuthenticationRequest
,由於rest-assured會自動幫你用jackson
將對象序列化成json字符串。固然也能夠將轉換好的json放到body,效果是同樣的。sql
返回結果被一個Response接住,以後就能夠用JsonPath獲取其中數據進行驗證。固然還有一種更直觀的辦法,能夠經過response.asString()
獲取完整的response,再反序列化成java對象進行驗證。數據庫
至此,最基本的IT就完成了。 在Jenkins增長一個stepgradle test
就能夠實現每次提交代碼都進行一次測試。json
這是最容易發生,一個項目有不少dev,每一個dev都會寫本身的IT case,那麼若是數據之間產生了影響怎麼辦。很容易理解,好比一個測試批量寫的場景,最後驗證方式是看寫的數據量是否是10w行。那麼另一個dev寫了其餘的case剛好也新增了一條數據到這張表,結果變成了10w+1行,那麼批量寫的case就跑不過了。api
爲了杜絕這種狀況,咱們採用每次跑完一個測試Class就將數據清空。既然是基於類的操做,能夠寫一個基類解決。
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles({Profiles.ENV_IT}) public abstract class BaseIntegrationTest { private static JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } @Value("${local.server.port}") protected int port; @Before public void setupEnv() { RestAssured.port = port; RestAssured.basePath = "/api"; RestAssured.baseURI = "http://localhost"; RestAssured.config = RestAssured.config().httpClient(HttpClientConfig.httpClientConfig().httpMultipartMode(HttpMultipartMode.BROWSER_COMPATIBLE)); } public void tearDownEnv() { given().contentType(ContentType.JSON) .when().post("/auth/logout"); } @AfterClass public static void cleanDB() throws SQLException { Resource resource = new ClassPathResource("/testdata/CleanDB.sql"); Connection connection = jdbcTemplate.getDataSource().getConnection(); ScriptUtils.executeSqlScript(connection, resource); connection.close(); } }
@AfterClass
中使用了jdbcTemplate執行了一個CleanDB.sql,經過這種方式清除全部測試數據。
@Value("${local.server.port}")
也要提一下,由於端口是隨機的,那麼Rest-Assured不知道請求要發到losthost的哪一個端口上,這裏使用@Value
獲取當前的端口號並設置到RestAssured.port
就解決了這個問題。
跑一次完整的IT,可能須要經歷數十個Class,數百個method,那麼若是一些數據是全部case都須要的,只有在全部case都跑完才須要清除怎麼辦?換句話說,這種數據清理不是基於類的,而是基於一次運行。好比初始用戶數據,城市庫等等
咱們耍了個小聰明,藉助了flyway
@Configuration @ConditionalOnClass({DataSource.class}) public class UpgradeAutoConfiguration { public static final String FLYWAY = "flyway"; @Bean(name = FLYWAY) @Profile({ENV_IT}) public UpgradeService cleanAndUpgradeService(DataSource dataSource) { UpgradeService upgradeService = new FlywayUpgradeService(dataSource); try { upgradeService.cleanAndUpgrade(); } catch (Exception ex) { LOGGER.error("Flyway failed!", ex); } return upgradeService; } }
能夠看到當Profile是IT的狀況下,flyway
會drop掉全部表並從新依次執行每次的upgrade腳本,由此建立完整的數據表,固然都是空的。在項目的test路徑下,增長一個版本極大的sql,這樣就可讓flyway
在最後插入共用的測試數據,例如src/test/resources/db/migration/V999.0.1__Insert_Users.sql
,完美的解決各類數據問題。
用Spring boot內置的測試服務能夠很快速的驗證API,我如今都不用把服務啓動再經過人工頁面點擊來測試本身的API,直接與前端同事溝通好Request的格式,寫個Case就能夠驗證。
固然這種方式也有一個不足就是不方便對系統進行壓力測試,以前在公司的API測試用例都是Jmeter寫的,作性能測試的時候會方便不少。
仍在尋找合適的跑性能的工具,若有推薦歡迎留言。