1. 程式人生 > >JUnit 5和Selenium基礎(三)

JUnit 5和Selenium基礎(三)

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

使用JUnit 5並行測試執行

JUnit 5帶有內建的並行測試執行支援。下面的命令將並行執行TodoMvcTests的測試方法:

./gradlew clean test --tests *TodoMvcTests -Djunit.jupiter.execution.parallel.enabled=true -Djunit.jupiter.execution.parallel.mode.default=concurrent

構建成功,在執行過程中,注意到兩個Chrome瀏覽器例項正在執行。在此執行中,所有測試的執行時間減少到原來的幾分之一:

> 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批註會更改行為,以便在所有測試之前初始化瀏覽器例項一次,並在所有測試之後關閉瀏覽器例項。

要應用@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();
}

當執行測試時,我們可以觀察到執行所有測試的時間大大減少了:

./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”,禁止第三方(騰訊雲除外)轉載、發表。

技術類文章精選

  • java一行程式碼列印心形
  • Linux效能監控軟體netdata中文漢化版
  • 效能測試框架第二版
  • 如何在Linux命令列介面愉快進行效能測試
  • 圖解HTTP腦圖
  • 將swagger文件自動變成測試程式碼
  • 基於java的直線型介面測試框架初探
  • Selenium 4.0 Alpha更新日誌
  • Selenium 4.0 Alpha更新實踐
  • 如何統一介面測試的功能、自動化和效能測試用例

非技術文章精選

  • 為什麼選擇軟體測試作為職業道路?
  • 寫給所有人的程式設計思維
  • 成為自動化測試的7種技能
  • 如何在DevOps引入自動化測試
  • Web端自動化測試失敗原因彙總
  • 如何在DevOps引入自動化測試
  • 測試人員常用藉口
  • API測試基礎
  • API自動化測試指南
  • 未來的QA測試工程師