使用 Selenium 大规模运行自动化测试





5.00/5 (1投票)
在本文中,我们将学习如何设置 Selenium WebDriver 和 Java,并在 LambdaTest 云网格上(串行和并行)运行 Web 自动化测试。
Selenium WebDriver 是一个开源的、跨平台的库,旨在帮助自动化浏览器测试。测试工程师使用它通过与 Web 浏览器和网页上的不同元素进行交互来执行自动化测试,以模拟用户在网站上的操作。
Selenium WebDriver 版本 4.6.0 中引入的 Selenium Manager 给测试自动化工程师带来了巨大的解脱,因为它具有“自带电池”的功能。
这样,Selenium Manager 无需设置驱动程序可执行文件路径或使用 WebDriverManager 等第三方库来启动浏览器。
随着 Selenium WebDriver 4.12.0 的发布,还增加了更多功能,例如针对 Chrome 和 Firefox 浏览器的自动化驱动程序管理和自动化浏览器管理。也就是说,如果您在运行测试的机器上没有安装浏览器,Selenium 将会安装浏览器、下载所需的浏览器驱动程序并运行测试。是不是很棒?
Selenium WebDriver 与 Java 拥有庞大的社区,因此自动化测试人员可以利用活跃的社区贡献和详细的文档来编写测试。当测试人员在自动化网站时遇到问题时,它还可以提供快速帮助。
在本博客中,我们将学习如何设置 Selenium WebDriver 和 Java,并在 LambdaTest 云网格上(串行和并行)运行 Web 自动化测试。
那么,让我们开始吧!
设置框架
在我们进行演示之前,让我们先组织一下稍后编写代码所需的一些内容。例如,编程语言、框架等。在本例中,我们将使用 Java 作为我们的编程语言,TestNG 作为我们的测试框架,并选择您喜欢的任何 IDE。
编程语言/工具/框架 | 版本 |
Java | 17 |
Selenium WebDriver | 4.12.1 |
TestNG | 7.8.0 |
Maven | 3.9.4 |
首先,我们需要在 IDE 中创建一个新的 Maven 项目。成功创建项目后,让我们在 pom.xml 文件中添加 Selenium WebDriver 和 TestNG 依赖项。
文件名:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.mfaisalkhatri</groupId>
<artifactId>selenium-lambdatest-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>selenium-lambdatest-demo</name>
<url>http://maven.apache.org</url>
<properties>
<selenium-java.version>4.12.1</selenium-java.version>
<testng.version>7.8.0</testng.version>
<maven.compiler.version>3.11.0</maven.compiler.version>
<surefire-version>3.1.2</surefire-version>
<java.release.version>17</java.release.version>
<maven.source.encoding>UTF-8</maven.source.encoding>
<suite-xml>testng.xml</suite-xml>
<argLine>-Dfile.encoding=UTF-8 -Xdebug -Xnoagent</argLine>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium-java.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<release>${java.release.version}</release>
<encoding>${maven.source.encoding}</encoding>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-version}</version>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<properties>
<property>
<name>usedefaultlisteners</name>
<value>false</value>
</property>
</properties>
<suiteXmlFiles>
<suiteXmlFile>${suite-xml}</suiteXmlFile>
</suiteXmlFiles>
<argLine>${argLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
在 pom.xml 中更新了依赖项后,项目设置就完成了。我们可以开始使用 Selenium WebDriver 和 Java 编写 Web 自动化测试了。
使用 Selenium WebDriver 和 Java 编写第一个测试用例
我们将使用 LambdaTest 的 Selenium Playground 上的输入表单来演示 Selenium WebDriver 和 Java 的工作原理。以下是我们将在其上工作的测试场景:
- 打开 LambdaTest 的 Selenium Playground
- 点击 输入表单演示 链接。
- 定位并输入输入表单演示页面上所有字段的值。
- 点击提交按钮。
- 断言表单成功提交后显示的“感谢您与我们联系,我们将尽快回复您。”消息。
配置设置
正如我们在前面关于设置框架的部分所讨论的,我们已经创建了 Maven 项目。以下是项目结构的截图:
我们将在 LambdaTest 云网格上的 Chrome 和 Firefox 浏览器上运行测试。
LambdaTest 是一个由 AI 驱动的测试编排和执行平台,它允许您在由 3000 多个真实桌面浏览器、浏览器版本和操作系统组成的在线 Selenium 网格上大规模执行测试自动化。它提供了一个强大、可扩展且高度安全的测试执行云平台,使开发和测试团队能够加快发布周期。通过启用并行测试,它将测试执行时间大大缩短。
创建一个 Java 类,并将其命名为 DriverManager
。此类包含的代码将帮助初始化浏览器,并设置 LambdaTest 功能,以便在云端跨不同的浏览器、浏览器版本和操作系统运行我们的测试。
文件名:DriverManager.java
public class DriverManager {
private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();
private static final String GRID_URL = "@hub.lambdatest.com/wd/hub";
private static final String LT_ACCESS_TOKEN = System.getProperty("LT_ACCESS_KEY");
private static final String LT_USERNAME = System.getProperty("LT_USERNAME");
public void createDriver(final Browsers browser) {
switch (browser) {
case FIREFOX -> setupFirefoxDriver();
case CHROME_CLOUD -> setupChromeInLambdaTest();
case FIREFOX_CLOUD -> setupFirefoxInLambdaTest();
default -> setupChromeDriver();
}
setupBrowserTimeouts();
}
public WebDriver getDriver() {
return DriverManager.DRIVER.get();
}
public void quitDriver() {
if (null != DRIVER.get()) {
getDriver().quit();
DRIVER.remove();
}
}
private HashMap<String, Object> ltOptions() {
final var ltOptions = new HashMap<String, Object>();
ltOptions.put("username", LT_USERNAME);
ltOptions.put("accessKey", LT_ACCESS_TOKEN);
ltOptions.put("resolution", "2560x1440");
ltOptions.put("selenium_version", "4.0.0");
ltOptions.put("build", "LambdaTest Scale Demo");
ltOptions.put("name", "LambdaTest tests at scale");
ltOptions.put("acceptInsecureCerts", true);
ltOptions.put("w3c", true);
ltOptions.put("plugin", "java-testNG");
return ltOptions;
}
private void setDriver(final WebDriver driver) {
DriverManager.DRIVER.set(driver);
}
private void setupBrowserTimeouts() {
getDriver().manage()
.timeouts()
.implicitlyWait(Duration.ofSeconds(20));
}
private void setupChromeDriver() {
setDriver(new ChromeDriver());
}
private void setupChromeInLambdaTest() {
final var browserOptions = new ChromeOptions();
browserOptions.setPlatformName("Windows 10");
browserOptions.setCapability("LT:Options", ltOptions());
try {
setDriver(
new RemoteWebDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)),
browserOptions));
} catch (final MalformedURLException e) {
throw new Error("Error setting up cloud browser in LambdaTest", e);
}
}
private void setupFirefoxInLambdaTest() {
final var browserOptions = new FirefoxOptions();
browserOptions.setPlatformName("Windows 10");
browserOptions.setCapability("LT:Options", ltOptions());
try {
setDriver(
new RemoteWebDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)),
browserOptions));
} catch (final MalformedURLException e) {
throw new Error("Error setting up cloud browser in LambdaTest", e);
}
}
private void setupFirefoxDriver() {
setDriver(new FirefoxDriver());
}
}
代码演练
在我们深入了解代码结果之前,让我们一步一步地理解代码。
步骤 1:使用 ThreadLocal
类实例化 WebDriver
实例。这确保每个线程都有自己的独立 WebDriver
实例。
步骤 2:ThreadLocal
的主要优点是确保安全的驱动程序设置,尤其是在并行运行测试时。它隔离了不同的线程,即使它们在同一个 ThreadLocal
对象上设置了不同的值,也能提供线程安全。
步骤 3:这种隔离有助于线程独立运行和隔离,从而防止冲突和问题。
在 LambdaTest 平台上运行测试时,需要设置特定的强制功能。您可以使用 LambdaTest 功能生成器来有效配置这些功能。这确保您的测试与 LambdaTest 平台兼容并能无缝运行。
功能名称 | 描述 |
GRID_URL | 在 LambdaTest 云网格上运行测试所需的远程 URL |
LT_USERNAME | LambdaTest 用户名 |
LT_ACCESS_KEY | LambdaTest 访问密钥 |
LambdaTest 功能生成器允许通过从 UI 中轻松选择所需配置来设置功能。相应地,它会生成可以轻松复制并粘贴到项目中的代码,从而轻松地开始浏览器自动化测试。
在 DriverManager 类中设置功能
使用 LambdaTest 功能生成器生成的功能已在 ltOptions()
方法中更新,该方法将在 setupChromeInLambdaTest()
和 setupFirefoxInLambdaTest()
方法中使用,以在 LambdaTest 云网格上为 Chrome 和 Firefox 浏览器分别设置功能。
setupChromeInLambdaTest() 方法
操作系统等附加参数将使用 setupChromeInLambdaTest()
方法设置。我们将使用“Windows 10”平台。Selenium 的 RemoteWebDriver()
方法将帮助我们在 LambdaTest 云上实例化一个新的 WebDriver 会话,该会话将通过传递 GRID_URL
、LT_USERNAME
和 LT_ACCESS_KEY
来创建。
同样,setupFirefoxInLambdaTest()
方法将帮助在 LambdaTest 云网格上的“Windows 10”平台上创建新的 Firefox 浏览器会话。
在 LambdaTest 云网格上,根据 testng.xml 文件中为浏览器传递的值。
成功创建浏览器会话后,setupBrowserTimeouts()
方法将有助于设置 20 秒的隐式等待超时。
隐式等待适用于页面上的所有 Web 元素,并被添加,以便驱动程序轮询页面直到找到元素或超时到期。
类似地,也可以添加显式等待来搜索 Web 元素,其中使用 ExpectedConditions
类通过调用显式等待来要求驱动程序等待特定条件。
至此,所有配置都已设置完毕,我们可以继续编写博客前面讨论过的测试场景的自动化测试了。
配置测试
在开始编写测试之前,我们先创建一个 BaseTest
类,该类将包含通用的配置设置,例如启动和关闭浏览器。
这个 BaseTest
类将被实际的测试类继承。
文件名:BaseTest.java
public class BaseTest {
protected DriverManager driverManager;
@BeforeClass
@Parameters("browser")
public void setup(final String browser) {
this.driverManager = new DriverManager();
this.driverManager.createDriver(Browsers.valueOf(browser.toUpperCase()));
}
@AfterClass
public void tearDown() {
this.driverManager.quitDriver();
}
}
TestNG 中的 @Parameters
注释已在此 BaseTest
类中使用,因为它有助于通过 testng.xml 配置多个参数,从而消除了反复更新代码的麻烦,因为测试可以在不修改代码的情况下运行在不同的浏览器上。可以在 testng.xml 中更新浏览器值,并据此执行测试。
测试场景的实现
为了快速回顾,我们将为以下测试场景编写测试:
已创建以下测试类 SeleniumDemoTests
,它将实现我们在此测试场景中讨论的所有步骤。
文件名:SeleniumDemoTests.java
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import java.sql.DriverManager;
public class SeleniumDemoTests extends BaseTest {
@Test
public void testInputForm() {
this.driverManager.getDriver().navigate().to("https://www.lambdatest.com/selenium-playground/");
final var mainPage = new MainPage(this.driverManager.getDriver());
mainPage.clickOnLink("Input Form Submit");
final var formDemoPage = new FormDemoPage(this.driverManager.getDriver());
formDemoPage.fillForm("Faisal", "faisal@gmail.com", "Pass@111", "LambdaTest",
"https://www.lambdatest.com", "India", "Mumbai",
"Sector 22, Lane 1", "Landmark zone",
"Maharashtra", "400001");
assertEquals(formDemoPage.successMessage(), "Thanks for contacting us, we will get back to you shortly.");
}
}
在编写测试时,使用了 Page Object Model,它涉及到创建两个需要测试的独立页面对象类:主页和输入表单演示页面。
MainPage
类包含 LambdaTest 的 Selenium Playground 主页的定位器方法。在此页面上,我们需要定位“输入表单提交”链接并单击它以打开输入表单演示页面。
文件名:MainPage.java
public class MainPage {
private final WebDriver driver;
public MainPage(final WebDriver driver) {
this.driver = driver;
}
public void clickOnLink(final String linkName) {
this.driver.findElement(By.linkText(linkName)).click();
}
}
为了定位“输入表单提交”链接,我们将使用 Chrome 浏览器中的开发者工具选项。
由于这是一个 href 链接
,我们可以使用 Selenium Selector LinkText
来定位此元素。为了使方法在定位此页面上的所有链接方面更健壮,已创建一个通用方法 clickOnLink()
,该方法接受字符串格式的链接名称,并使用 LinkText
选择器策略定位并单击该链接。这样,我们就无需为定位此页面上的其他链接添加重复的代码行。
接下来,让我们转到点击“输入表单提交”链接后将打开的“表单演示”页面。
已创建 FormDemoPage
类来保存表单演示页面的所有定位器。
文件名:FormDemoPage.java
public class FormDemoPage {
private final WebDriver driver;
private final WebDriverWait wait;
public FormDemoPage(final WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(20));
}
private WebElement nameField() {
return this.driver.findElement(By.id("name"));
}
private WebElement emailField() {
return this.driver.findElement(By.id("inputEmail4"));
}
private WebElement passwordField() {
return this.driver.findElement(By.id("inputPassword4"));
}
private WebElement companyField() {
return this.driver.findElement(By.id("company"));
}
private WebElement websiteField() {
return this.driver.findElement(By.id("websitename"));
}
private WebElement country() {
return this.driver.findElement(By.name("country"));
}
private void countryName(final String countryName) {
country().click();
new Select(country()).selectByVisibleText(countryName);
}
private WebElement cityField() {
return this.driver.findElement(By.id("inputCity"));
}
private WebElement addressLineOneField() {
return this.driver.findElement(By.name("address_line1"));
}
private WebElement addressLineTwoField() {
return this.driver.findElement(By.name("address_line2"));
}
private WebElement stateField() {
return this.driver.findElement(By.id("inputState"));
}
private WebElement zipCodeField() {
return this.driver.findElement(By.id("inputZip"));
}
private WebElement submitBtn() {
return this.driver.findElement(By.cssSelector("#seleniumform button[type=\"submit\"]"));
}
public String successMessage() {
return this.wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("p.success-msg"))).getText();
}
public void fillForm(final String name, final String email, final String password, final String company,
final String website, final String country, final String city, final String addressLineOne,
final String addressLineTwo, final String state, final String zipCode) {
this.nameField().sendKeys(name);
this.emailField().sendKeys(email);
this.passwordField().sendKeys(password);
this.companyField().sendKeys(company);
this.websiteField().sendKeys(website);
this.countryName(country);
this.cityField().sendKeys(city);
this.addressLineOneField().sendKeys(addressLineOne);
this.addressLineTwoField().sendKeys(addressLineTwo);
this.stateField().sendKeys(state);
this.zipCodeField().sendKeys(zipCode);
this.submitBtn().click();
}
}
输入表单演示页面上有多个文本输入字段,如 Name
、Email
、Password
、Company
、Website
、City
、State
等。所有这些字段都使用 ID 定位器进行定位。例如,下面的 nameField()
方法返回 Name
字段的 WebElement
。同样,为定位此页面上的其他字段创建了单独的方法。
Country
下拉菜单是一个单选下拉菜单,其中包含已填充的国家列表。
如果我们查看表单演示页面的 DOM,我们可以看到它上面有一个 Select
标签。
这清楚地表明我们可以使用 Selenium 的 Select
类与此字段进行交互。与此字段交互的策略是首先定位 Country
字段,然后从该字段中选择国家名称的可见文本。
country()
方法将定位并返回 Country
字段的 WebElement
。接下来,countryName()
方法将选择在其参数中提供的 Country
名称。
需要定位页面上的提交按钮,以便我们可以提交表单。
查看页面的 DOM,可以看到提交按钮没有 ID 或 name 属性。在这里,我们可以使用 CSS Selector 定位器策略,通过使用选择器 - #seleniumform button[type = "submit"]
来定位提交按钮。
#seleniumform
是表单演示页面上 Form
的 ID 属性,在此之中,具有“submit”值的 type
属性的 button 标签
可用于提交按钮。
fillForm()
方法将执行与表单演示页面上所有字段的交互并单击提交按钮。实际测试中的参数将是此方法提供的测试数据。
接下来,我们将定位表单成功提交后显示的成功消息文本。现在,关键在于该元素将在表单提交几秒钟后可见。因此,在 Selenium 开始定位元素之前,我们将不得不等待几秒钟。如果不等待,由于 Selenium 将在表单提交后直接开始搜索文本,我们将收到成功消息文本的 NoSuchElementException
。
为了处理这种等待,我们将使用 Selenium 中的显式等待,从 expected_conditions
类调用 presenceOfElementLocatedBy()
方法。
successMessage()
方法将等待 WebElement
存在,一旦可用,它将定位 WebElement
并以 String 格式返回其文本。
以下是我们在此博客的早期部分讨论过的实现了测试场景的测试。
测试将首先导航到 LambdaTest 的 Selenium Playground 网站。接下来,它将单击主页上的“输入表单提交”链接。加载表单演示页面后,它将使用 FormDemoPage
类中的 fillForm()
方法填充表单。
测试的最后一条语句用于执行断言。它将检查成功消息文本是否等于“感谢您与我们联系,我们将尽快回复您。”
在 LambdaTest 云网格上使用 Selenium 大规模运行自动化测试
以下 testng.xml 文件将用于运行测试。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Selenium WebDriver Demo Suite for LambdaTests" parallel="tests">
<test name="Selenium Playground - Input Form Demo Test on Chrome">
<parameter name="browser" value="chrome"/>
<classes>
<class name="io.github.mfaisalkhatri.SeleniumDemoTests">
<methods>
<include name="testInputForm"/>
</methods>
</class>
</classes>
</test>
<test name="Selenium Playground - Input Form Demo Test on Firefox">
<parameter name="browser" value="firefox"/>
<classes>
<class name="io.github.mfaisalkhatri.SeleniumDemoTests">
<methods>
<include name="testInputForm"/>
</methods>
</class>
</classes>
</test>
<test name="Selenium Playground - Input Form Demo Test on LambdaTest Chrome">
<parameter name="browser" value="chrome_cloud"/>
<classes>
<class name="io.github.mfaisalkhatri.SeleniumDemoTests">
<methods>
<include name="testInputForm"/>
</methods>
</class>
</classes>
</test>
<test name="Selenium Playground - Input Form Demo Test on LambdaTest Firefox">
<parameter name="browser" value="firefox_cloud"/>
<classes>
<class name="io.github.mfaisalkhatri.SeleniumDemoTests">
<methods>
<include name="testInputForm"/>
</methods>
</class>
</classes>
</test>
</suite>
testng.xml 文件中总共有 4 个不同的测试块。所有测试都将并行运行。并行执行测试有助于节省测试执行时间,因为所有测试都是并发执行的。
我们需要在 testng.xml 中为执行并行测试提及 parallel="tests"
。在这里,testng.xml 中更新的所有测试块都将并行执行。
第一个和第二个测试块将在本地机器上的 Chrome 和 Firefox 浏览器上分别执行测试场景。第三个和第四个测试块将在 LambdaTest 云网格上的 Chrome 和 Firefox 浏览器上分别执行测试场景。
由于我们将 ThreadLocal
类整合到我们的代码中,因此我们的测试执行是线程安全的。因此,无需担心测试会话重叠问题。
使用 IntelliJ IDE 执行的测试的屏幕截图
可以在 LambdaTest Dashboard 上查看在 LambdaTest 云网格上执行的测试。它通过在 LambdaTest Analytics 中提供测试运行的分步详细信息,对测试执行情况提供了公平的可见性。还可以查看视频录制、设备日志、屏幕截图等详细信息。
查看下面的屏幕截图,它们将为您提供有关 Web 自动化测试仪表板的公平见解。
LambdaTest Dashboard
以下屏幕截图显示了构建和进行的测试的详细信息。每项测试包括测试名称、浏览器名称、浏览器版本、操作系统名称、相应的操作系统版本和屏幕分辨率。
它还包含测试的视频,可以更好地了解测试在浏览器上如何运行。
在 LambdaTest 云上的 Chrome 浏览器上运行的测试
在 LambdaTest 云上的 Firefox 浏览器上运行的测试
结论
Selenium WebDriver 是一个流行的 Web 自动化框架,可自动执行与 Web 浏览器相关的测试。测试自动化有助于更快地获得反馈并快速运行所有回归和功能测试。
通过将 Selenium 与 LambdaTest 等云提供商集成,我们可以在不同浏览器和版本上大规模运行 Web 自动化测试,而无需担心操作系统和浏览器安装,因为所有基础设施都是按需提供的。
测试执行成功完成后,可以看到测试的详细见解,以及所有详细信息,这些详细信息有助于向软件团队和利益相关者进行报告。