在這一部分教程中,將介紹JUnit 5的其餘功能,這些功能將經過並行運行測試,配置測試順序和建立參數化測試來幫助減小測試的執行時間。還將介紹如何利用Selenium Jupiter功能,例如經過系統屬性進行測試執行配置,單個瀏覽器會話測試以加快測試執行速度或捕獲測試中的屏幕截圖,AssertJ庫的基本Demo。java
JUnit 5帶有內置的並行測試執行支持。下面的命令將並行運行TodoMvcTests
的測試方法:shell
./gradlew clean test --tests *TodoMvcTests -Djunit.jupiter.execution.parallel.enabled=true -Djunit.jupiter.execution.parallel.mode.default=concurrent
編程
構建成功,在執行過程當中,注意到兩個Chrome瀏覽器實例正在運行。在此運行中,全部測試的執行時間減小到原來的幾分之一:api
> Task :test demos.selenium.todomvc.TodoMvcTests > createsTodo() PASSED demos.selenium.todomvc.TodoMvcTests > createsTodosWithSameName() PASSED demos.selenium.todomvc.TodoMvcTests > togglesAllTodosCompleted() PASSED demos.selenium.todomvc.TodoMvcTests > togglesTodoCompleted() PASSED demos.selenium.todomvc.TodoMvcTests > clearsCompletedTodos() PASSED demos.selenium.todomvc.TodoMvcTests > editsTodo() PASSED demos.selenium.todomvc.TodoMvcTests > removesTodo() PASSED BUILD SUCCESSFUL in 10s 4 actionable tasks: 4 executed
通常來說,自動化測試應該可以獨立運行而且沒有特定的順序,而且測試結果不該依賴於先前測試的結果。可是在某些狀況下測試執行須要依賴特定順序。數組
默認狀況下,在JUnit 5中,測試方法的執行在構建之間是無序的,所以非肯定性的。可是可使用內置方法定購器或經過建立自定義定購器來調整執行順序以知足測試的需求。咱們將使用@Order
批註來提供測試方法的排序,並使用註釋類,@TestMethodOrder
以指示JUnit 5
方法已排序。瀏覽器
@ExtendWith(SeleniumExtension.class) @SingleSession @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DisplayName("Managing Todos") class TodoMvcTests { @Test @Order(1) @DisplayName("test001") void createsTodo() { } @Test @Order(2) @DisplayName("test002") void createsTodosWithSameName() { } }
對於TodoMvcTests
類中的每一個測試,都會啓動一個新的Chrome
瀏覽器實例,並在每一個測試以後將其關閉。此行爲致使整個套件的執行花費了至關多的時間。Selenium Jupiter
附帶了一個簡單的類級別註釋,能夠修改這項功能。@SingleSession
批註會更改行爲,以便在全部測試以前初始化瀏覽器實例一次,並在全部測試以後關閉瀏覽器實例。mvc
要應用@SingleSession
須要稍微修改測試類,而後將驅動程序對象注入構造函數中而不是@BeforeEach
方法中。咱們還須要注意每次測試的正確狀態。這能夠經過清除@AfterEach
方法中存儲待辦事項的本地存儲來完成。我還建立了一個字段driver
,該字段保留全部測試中使用的驅動程序對象實例。框架
private final ChromeDriver driver; public TodoMvcTests(ChromeDriver driver) { this.driver = driver; this.todoMvc = PageFactory.initElements(driver, TodoMvcPage.class); this.todoMvc.navigateTo(); } @AfterEach void storageCleanup() { driver.getLocalStorage().clear(); }
當執行測試時,咱們能夠觀察到執行全部測試的時間大大減小了:dom
./gradlew clean test > Task :test demos.selenium.todomvc.TodoMvcTests > editsTodo() PASSED demos.selenium.todomvc.TodoMvcTests > togglesTodoCompleted() PASSED demos.selenium.todomvc.TodoMvcTests > createsTodo() PASSED demos.selenium.todomvc.TodoMvcTests > removesTodo() PASSED demos.selenium.todomvc.TodoMvcTests > togglesAllTodosCompleted() PASSED demos.selenium.todomvc.TodoMvcTests > createsTodosWithSameName() PASSED demos.selenium.todomvc.TodoMvcTests > clearsCompletedTodos() PASSED demos.selenium.todomvc.SeleniumTest > projectIsConfigured(ChromeDriver) PASSED BUILD SUCCESSFUL in 9s 3 actionable tasks: 3 executed
提示:若是您但願從選定的類中運行測試,則可使用Gradle測試任務隨附的測試過濾。例如,此命令將僅運行來自TodoMvcTests
類的測試:./gradlew clean test --tests *.todomvc.TodoMvcTests
函數
若是你如今嘗試使用JUnit 5
並行執行測試,在並行執行中,每種方法都須要單獨的驅動程序實例,而且@SingleSession
啓用後,咱們將爲全部測試共享一個實例。爲了解決這個問題,須要運行測試配置並行執行,爲了讓頂級類並行運行,但方法在同一線程中。
只需複製TodoMvcTests
類,而後嘗試如下命令:
./gradlew clean test --tests *TodoMvcTests -Djunit.jupiter.execution.parallel.enabled=true -Djunit.jupiter.execution.parallel.mode.default=same_thread -Djunit.jupiter.execution.parallel.mode.classes.default=concurrent
在執行過程當中,應該看到正在運行並在終端中輸出如下內容:
<===========--> 87% EXECUTING [3s] > :test > 0 tests completed > :test > Executing test demos.selenium.todomvc.MoreTodoMvcTests > :test > Executing test demos.selenium.todomvc.EvenMoreTodoMvcTests > :test > Executing test demos.selenium.todomvc.TodoMvcTests
在當前測試中,咱們將ChromeDriver
直接注入測試類。可是在某些狀況下,咱們但願對注入的驅動程序有更多的控制,而咱們寧願注入WebDriver
(接口)並稍後決定應該注入哪一個驅動程序實例。咱們還須要更改storageCleanup()
方法,由於通用WebDriver
不提供直接的localStorage
訪問:
public TodoMvcTests(WebDriver driver) { this.driver = driver; this.todoMvc = PageFactory.initElements(driver, TodoMvcPage.class); this.todoMvc.navigateTo(); } @AfterEach void storageCleanup() { ((JavascriptExecutor) driver).executeScript("window.localStorage.clear()"); }
如今,要在運行時更改瀏覽器類型,咱們須要調整sel.jup.default.browserconfig
屬性。
配置JUnit 5
和Selenium Jupiter
的經常使用方法之一是經過Java系統屬性。可使用屬性文件以編程方式完成此操做,也可使用-Dswitch
將屬性直接傳遞給JVM 。爲了確保在執行Gradle
時傳遞給JVM的屬性在測試中可用,咱們須要進行build.gradle
以下修改:
test { systemProperties System.getProperties() useJUnitPlatform() testLogging { events "passed", "skipped", "failed" } }
當運行命令時./gradlew clean test -Dprop=value
,該屬性將在測試中可用。經過上述更改,咱們能夠選擇瀏覽器類型來運行測試:
./gradlew clean test --tests *TodoMvcTests -Dsel.jup.default.browser=firefox
Selenium Jupiter
容許在測試結束時保存屏幕截圖-始終或僅在失敗時保存。您還能夠自定義輸出目錄和格式。./gradlew clean test --tests *TodoMvcTests -Dsel.jup.default.browser=firefox -Dsel.jup.screenshot.at.the.end.of.tests=true -Dsel.jup.screenshot.format=png -Dsel.jup.output.folder=/tmp
參數化單元測試的整體思路是針對不一樣的測試數據運行相同的測試方法。要在JUnit 5中建立參數化測試,請使用註釋測試方法,@ParameterizedTest並提供該測試方法的參數源。有幾種可用的參數來源,包括:
爲了在測試中使用上述CSV文件,咱們須要在測試中加上@ParameterizedTest註釋(而不是@Test),而後在@CsvFileSource註釋中指向文件:
@ParameterizedTest @CsvFileSource(resources = "/todos.csv", numLinesToSkip = 1, delimiter = ';') @DisplayName("Creates Todo with given name") void createsTodo(String todo) { todoMvc.createTodo(todo); assertSingleTodoShown(todo); }
在下一個示例中,咱們將使用如下CSV格式存儲在src/test/resources
目錄中:
todo;done Buy the milk;false Clean up the room;true Read the book;false
CSV文件中的每一個記錄都有兩個字段:name
和done
。在上述測試中,僅使用待辦事項的名稱。可是咱們固然可使測試複雜一點,並同時使用這兩個屬性:
@ParameterizedTest(name = "{index} - {0}, done = {1}" ) @CsvFileSource(resources = "/todos.csv", numLinesToSkip = 1, delimiter = ';') @DisplayName("test003") void createsAndRemovesTodo(String todo, boolean done) { todoMvc.createTodo(todo); assertSingleTodoShown(todo); todoMvc.showActive(); assertSingleTodoShown(todo); if (done) { todoMvc.completeTodo(todo); assertNoTodoShown(todo); todoMvc.showCompleted(); assertSingleTodoShown(todo); } todoMvc.removeTodo(todo); assertNoTodoShown(todo); }
JUnit 5
具備許多內置的斷言,在實際工做中,可能須要的超出JUnit 5
所能提供的。在這種狀況下,建議使用AssertJ庫。AssertJ
是一個Java庫,提供了一組豐富的斷言,真正有用的錯誤消息,提升了測試代碼的可讀性,而且設計爲IDE中容易使用。
AssertJ的一些功能:
要在項目中使用AssertJ,咱們須要向中添加單個依賴項build.gradle
:
testCompile('org.assertj:assertj-core:3.13.2')
首先,咱們須要靜態導入org.assertj.core.api.Assertions.*
並使用如下assertThat方法完成代碼:assertThat(objectUnderTest)
.
例如將assertThat(todoMvc.getTodosLeft()).isEqualTo(3);
使用AssertJ
而不是assertEquals(3, todoMvc.getTodosLeft());
純JUnit 5
或assertThat(todoMvc.todoExists(readTheBook)).isTrue()
來編寫assertTrue(todoMvc.todoExists(readTheBook))
。
使用複雜類型甚至更好:
todoMvc.createTodos(buyTheMilk, cleanupTheRoom, readTheBook); assertThat(todoMvc.getTodos()) .hasSize(3) .containsSequence(buyTheMilk, cleanupTheRoom, readTheBook);