JUnit 5和Selenium基礎(三)

在這一部分教程中,將介紹JUnit 5的其餘功能,這些功能將經過並行運行測試,配置測試順序和建立參數化測試來幫助減小測試的執行時間。還將介紹如何利用Selenium Jupiter功能,例如經過系統屬性進行測試執行配置,單個瀏覽器會話測試以加快測試執行速度或捕獲測試中的屏幕截圖,AssertJ庫的基本Demo。java

使用JUnit 5並行測試執行

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測試執行順序

通常來說,自動化測試應該可以獨立運行而且沒有特定的順序,而且測試結果不該依賴於先前測試的結果。可是在某些狀況下測試執行須要依賴特定順序。數組

默認狀況下,在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() {
 
    }
  
}

使用Selenium Jupiter的單個瀏覽器會話

對於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

Selenium Jupiter的驅動程序配置

在當前測試中,咱們將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 5Selenium 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進行參數化測試

參數化單元測試的整體思路是針對不一樣的測試數據運行相同的測試方法。要在JUnit 5中建立參數化測試,請使用註釋測試方法,@ParameterizedTest並提供該測試方法的參數源。有幾種可用的參數來源,包括:

  • @ValueSource –提供對文字值數組(例如,int,字符串等)的訪問。
  • @MethodSource –提供對從工廠方法返回的值的訪問
  • @CsvSource –從一個或多個提供的CSV行中讀取逗號分隔值(CSV)
  • @CsvFileSource –用於加載逗號分隔值(CSV)文件

爲了在測試中使用上述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文件中的每一個記錄都有兩個字段:namedone。在上述測試中,僅使用待辦事項的名稱。可是咱們固然可使測試複雜一點,並同時使用這兩個屬性:

@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);
}

使用AssertJ更好的斷言

JUnit 5具備許多內置的斷言,在實際工做中,可能須要的超出JUnit 5所能提供的。在這種狀況下,建議使用AssertJ庫。AssertJ是一個Java庫,提供了一組豐富的斷言,真正有用的錯誤消息,提升了測試代碼的可讀性,而且設計爲IDE中容易使用。

AssertJ的一些功能:

  • 對許多Java類型的流利斷言,包括日期,集合,文件等。
  • SoftAssertions(相似於JUnit 5的assertAll)
  • 複雜領域比較
  • 能夠輕鬆擴展–自定義條件和自定義斷言

要在項目中使用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 5assertThat(todoMvc.todoExists(readTheBook)).isTrue()來編寫assertTrue(todoMvc.todoExists(readTheBook))

使用複雜類型甚至更好:

todoMvc.createTodos(buyTheMilk, cleanupTheRoom, readTheBook);
 
assertThat(todoMvc.getTodos())
        .hasSize(3)
        .containsSequence(buyTheMilk, cleanupTheRoom, readTheBook);

  • 鄭重聲明:文章首發於公衆號「FunTester」,禁止第三方(騰訊雲除外)轉載、發表。

技術類文章精選

非技術文章精選

相關文章
相關標籤/搜索