Selenium是一组支持浏览器自动化的工具和库,主要用于Web应用程序测试。 Selenium的组件之一是Selenium WebDriver,它提供客户端库,JSON有线协议(与浏览器驱动程序进行通信的协议)和浏览器驱动程序。 Selenium WebDriver的主要优点之一是,它受所有主要编程语言的支持,并且可以在所有主要操作系统上运行。
在带有Selenium WebDriver的JUnit 5的这一部分–教程中,您将了解JUnit 5的其他功能,这些功能将通过并行运行测试,配置测试顺序和创建参数化测试来帮助您减少测试的执行时间。
您还将学习如何利用Selenium Jupiter功能,例如通过系统属性进行测试执行配置,单个浏览器会话测试以加快测试执行速度或捕获测试中的屏幕截图。 最后,您将学习如何将AssertJ库添加到您的项目。
关于本教程
您正在阅读带有Selenium WebDriver的JUnit 5的第三部分-教程 。
本教程中的所有文章:
- 第1部分– 从头开始设置项目–使用JUnit 5和Jupiter Selenium进行Gradle
- 第2部分– 使用Selenium内置的
PageFactory
实现Page Object Pattern - 第3部分– 改进项目配置–并行执行测试,测试执行顺序,参数化测试,AssertJ等
该教程的源代码可以在Github上找到
使用JUnit 5并行测试执行
JUnit 5带有内置的并行测试执行支持。
下面的命令将并行运行TodoMvcTests的测试方法:
./gradlew clean test --tests *TodoMvcTests -Djunit.jupiter.execution.parallel.enabled= true -Djunit.jupiter.execution.parallel.mode. default =concurrent
构建成功,在执行过程中,您应该注意到两个Chrome浏览器实例正在运行。 在此运行中,所有测试的执行时间减少到10秒:
> Task :test pl.codeleak.demos.selenium.todomvc.TodoMvcTests > createsTodo() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > createsTodosWithSameName() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > togglesAllTodosCompleted() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > togglesTodoCompleted() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > clearsCompletedTodos() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > editsTodo() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > removesTodo() PASSED BUILD SUCCESSFUL in 10s 4 actionable tasks: 4 executed
提示:有关更多选项,请查阅文档: https : //junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution
使用JUnit 5测试执行顺序
自动化测试应该能够独立运行并且没有特定的顺序,并且测试结果不应依赖于先前测试的结果。 但是在某些情况下可以证明测试执行的特定顺序。
默认情况下,在JUnit 5中,测试方法的执行在构建之间是可重复的,因此是确定性的,但是该算法是故意不明显的(作为库状态的作者)。 幸运的是,可以使用内置方法定购器或通过创建自定义定购器来调整执行顺序以适应我们的需求。 我们将使用@Order
批注来提供测试方法的排序,并使用@TestMethodOrder
注释类,以指示JUnit 5方法已排序。
@ExtendWith (SeleniumExtension. class ) @SingleSession @TestMethodOrder (MethodOrderer.OrderAnnotation. class ) @DisplayName ( "Managing Todos" @DisplayName "Managing Todos" ) class TodoMvcTests {
@Test
@Order ( 1 )
@DisplayName ( "Creates Todo with given name" )
void createsTodo() {
}
@Test
@Order ( 2 )
@DisplayName ( "Creates Todos all with the same name" @DisplayName "Creates Todos all with the same name" )
void createsTodosWithSameName() {
}
// rest of the methods omitted for readability }
在本文中阅读有关JUnit 5中测试执行顺序的更多信息: https : //blog.codeleak.pl/2019/03/test-execution-order-in-junit-5.html
使用Selenium Jupiter的单个浏览器会话
您可能已经注意到,对于TodoMvcTests
类中的每个测试, TodoMvcTests
启动一个新的Chrome浏览器实例,并在每个测试之后将其关闭。 此行为导致整个套件的执行花费了相当多的时间(上一次执行需要27秒)。 Selenium Jupiter附带了一个方便的类级别注释,可以更改此行为。 @SingleSession
注释会更改行为,以便在所有测试之前初始化浏览器实例一次,并在所有测试之后关闭浏览器实例。
要应用@SingleSession
我们需要略微修改测试类,并将驱动程序对象注入到构造函数中,而不是注入到@BeforeEach
方法中。 我们还需要注意每次测试的正确状态。 这可以通过清除@AfterEach
方法中@AfterEach
存储的本地存储来完成。 我还创建了一个现场driver
,该驱动driver
保留了所有测试中使用的驱动程序对象实例。
我使用注入到@BeforeEach
和@AfterEach
方法中的驱动程序测试了@SingleSession
,但似乎这没有按预期方式工作,并且每次执行新测试时,都会创建一个新的驱动程序实例。 我相信这是库的另一个设计缺陷。
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 pl.codeleak.demos.selenium.todomvc.TodoMvcTests > editsTodo() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > togglesTodoCompleted() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > createsTodo() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > removesTodo() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > togglesAllTodosCompleted() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > createsTodosWithSameName() PASSED pl.codeleak.demos.selenium.todomvc.TodoMvcTests > clearsCompletedTodos() PASSED pl.codeleak.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
在执行过程中,您应该看到3个浏览器正在运行并在终端中输出,类似于以下内容:
<===========--> 87 % EXECUTING [3s] > :test > 0 tests completed > :test > Executing test pl.codeleak.demos.selenium.todomvc.MoreTodoMvcTests > :test > Executing test pl.codeleak.demos.selenium.todomvc.EvenMoreTodoMvcTests > :test > Executing test pl.codeleak.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.browser
config属性。
配置JUnit 5和Selenium Jupiter的常用方法之一是通过Java系统属性。 可以使用属性文件以编程方式完成此操作,也可以使用-D
开关将属性直接传递给JVM。 为了确保在执行Gradle时传递给JVM的属性在测试中可用,我们需要修改build.gradle
,如下所示:
test {
// Make system properties available in tests
systemProperties System.getProperties()
useJUnitPlatform()
testLogging {
events "passed" , "skipped" , "failed"
} }
到目前为止,当您运行./gradlew clean test -Dprop=value
类的命令时,该属性prop
将在测试中可用。
通过上述更改,我们可以选择浏览器类型来运行测试:
./gradlew clean test --tests *TodoMvcTests -Dsel.jup. default .browser=firefox
使用Selenium Jupiter保存屏幕截图
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
提示:有关更多选项,请查阅文档: https : //bonigarcia.github.io/selenium-jupiter/#screenshots
使用JUnit 5进行参数化测试
参数化单元测试的总体思路是针对不同的测试数据运行相同的测试方法。 要在JUnit 5中创建参数化测试,请使用@ParameterizedTest
注释测试方法,并提供该测试方法的参数源。 有几种可用的参数来源,包括:
-
@ValueSource
–提供对文字值数组(即短裤,整数,字符串等)的访问。 -
@MethodSource
–提供对从工厂方法返回的值的访问 -
@CsvSource
–从一个或多个提供的CSV行中读取逗号分隔值(CSV) -
@CsvFileSource
–用于加载逗号分隔值(CSV)文件
在下一个示例中,我们将使用存储在src/test/resources
目录中的以下CSV:
todo;done Buy the milk; false Clean up the room; true Read the book; false
为了在测试中使用上述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文件中的每个记录都有两个字段: name
和done
。 在上述测试中,仅使用待办事项的名称。 但是我们当然可以使测试复杂一点,并同时使用这两个属性:
@ParameterizedTest (name = "{index} - {0}, done = {1}" ) @CsvFileSource (resources = "/todos.csv" , numLinesToSkip = 1 , delimiter = ';' ) @DisplayName ( "Creates and optionally removes Todo with given name" ) void createsAndRemovesTodo(String todo, done) { 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); }
请注意,同一测试类别/
了解更多关于这篇文章中参数的测试: https://blog.codeleak.pl/2017/06/cleaner-parameterized-tests-with-junit-5.html也经历了JUnit 5文档: https://开头的JUnit .org / junit5 / docs / current / user-guide /#writing-tests-parameterized-tests
使用AssertJ更好的断言
JUnit 5具有许多内置的断言,但是当实际工作开始时,您可能需要的远远超出JUnit 5所能提供的。 在这种情况下,我建议使用AssertJ库。 AssertJ AssertJ是一个Java库,提供了一组丰富的断言,真正有用的错误消息,提高了测试代码的可读性,并且设计为在您喜欢的IDE中非常容易使用。
AssertJ的一些功能:
- 对许多Java类型的流利断言,包括日期,集合,文件等。
- SoftAssertions(类似于JUnit 5的assertAll)
- 复杂领域比较
- 可以轻松扩展–自定义条件和自定义断言
要在项目中使用AssertJ,我们需要向build.gradle
添加一个依赖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);
访问官方文档以了解有关AssertJ的更多信息: https ://assertj.github.io/doc/
提示:阅读有关将AssertJ与JUnit 5集成的更多信息: https ://blog.codeleak.pl/2017/11/junit-5-meets-assertj.html
摘要
在本文中,我介绍了如何利用JUnit 5的内置功能来提高项目配置的速度,但不仅限于速度。 您还了解了如何利用某些Selenium Jupiter功能来改善项目。
JUnit 5和Selenium Jupiter似乎已经提供了更多的功能。 查看两个项目文档,并找出对当前或将来的项目有用的其他内容:
- https://bonigarcia.github.io/selenium-jupiter/
- https://junit.org/junit5/
翻译自: https://www.javacodegeeks.com/2019/12/junit-5-and-selenium-improving-project-configuration.html