在对Angular应用程序进行浏览器内的用户界面(UI)测试时,会面临诸多难题。其中,异步操作频繁出现,加载屏幕上常常带有加载指示器或遮罩层,其出现和消失的时间难以预测,这给测试工作增加了不少难度。

Selenium是一款优秀的UI测试工具,不过它没有内置自动等待加载指示器消失的机制。而Selenide在这方面具有独特优势,它能够隐式地等待加载屏幕消失,为我们的测试工作提供了极大的便利。接下来,让我们深入了解一下如何利用这两款工具进行Angular应用的UI测试。

一、Selenium与Selenide在处理加载屏幕上的差异

在使用纯Selenium进行测试时,高效处理加载屏幕是个不小的挑战。由于Selenium没有自动等待加载指示器消失的内置功能,测试人员通常需要手动编写显式等待代码。例如:

// 创建一个等待对象,最长等待10秒 WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 等待指定ID的元素消失 wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("dimmer-element-id"))); 

这种方式需要手动定义加载条件,不仅容易出错,还会增加测试代码的复杂性。

相比之下,Selenide具备隐式等待加载屏幕消失的能力。这意味着在加载屏幕完全消失之前,测试脚本不会继续执行。这种特性使测试更加稳定,同时减少了不必要的显式等待代码。以下是使用Selenide查找元素的示例代码:

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codeborne.selenide.SelenideElement; import static com.codeborne.selenide.Selenide.$; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import com.codeborne.selenide.Condition; @Slf4j public class ElementUtils { //... // 根据定位器查找元素,并设置超时时间 public static SelenideElement findElement(By by, int timeout) { log.info("Finding element: {}", by); try { // 等待元素可见,超时时间为传入的timeout return $(by).shouldBe(Condition.visible, Duration.ofSeconds(timeout)); } catch (AssertionError e) { log.warn("Element not found within timeout: {}", e.getMessage()); return null; } } } 

二、配置Maven依赖

Selenide是基于Selenium开发的,因此在项目中使用Selenide时,需要同时引入Selenium和Selenide的依赖,以及其他必要的依赖项。在pom.xml文件中添加如下配置:

<properties> <!-- WebDriverManager版本 --> <webdrivermanager.version>5.9.2</webdrivermanager.version> <!-- Selenium版本 --> <selenium.version>4.8.1</selenium.chrome.version> <!-- Selenide版本 --> <selenide.version>7.7.0</selenide.version> </properties> <dependencies> <!-- WebDriverManager依赖 --> <dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>${webdrivermanager.version}</version> </dependency> <!-- Selenium Java依赖 --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> <!-- Selenium Chrome驱动依赖 --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-chrome-driver</artifactId> <version>${selenium.version}</version> </dependency> <!-- Selenide依赖 --> <dependency> <groupId>com.codeborne</groupId> <artifactId>selenide</artifactId> <version>${selenide.version}</version> </dependency> <!-- 可在此处添加JUnit 5依赖 --> </dependencies> 

如果使用Gradle构建项目,则在build.gradle文件中添加以下依赖:

dependencies { // WebDriverManager依赖 testImplementation "io.github.bonigarcia:webdrivermanager:5.9.2" // Selenium Java依赖 testImplementation "org.seleniumhq.selenium:selenium-java:4.8.1" // Selenium Chrome驱动依赖 testImplementation "org.seleniumhq.selenium:selenium-chrome-driver:4.8.1" // Selenide依赖 testImplementation "com.codeborne:selenide:7.7.0" } 

三、设置WebDriver

如果直接依赖测试机器上安装的浏览器,很容易出现浏览器版本不匹配的问题。为了避免这种情况,强烈推荐使用WebDriverManager库。它是一个开源的Java库,能够全自动地管理(包括下载、安装和维护)Selenium WebDriver所需的驱动程序。

首先,在项目中引入WebDriverManager依赖:

<dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>${webdrivermanager.version}</version> </dependency> 

在代码中,通过WebDriverManager API选择对应的驱动管理器(例如,使用chromedriver()选择Chrome驱动),并调用setup()方法来自动化驱动管理:

import org.junit.jupiter.api.BeforeAll; import io.github.bonigarcia.wdm.WebDriverManager; public class LoginTest { @BeforeAll static void setup() { // 初始化Chrome驱动 WebDriverManager.chromedriver().setup(); } } 

执行setup()方法时,它会自动检测机器上安装的浏览器版本,然后通过多种方式找到与之匹配的驱动版本。找到合适的驱动版本后,WebDriverManager会将驱动下载到本地缓存目录(位于~/.cache/selenium),并通过Java系统属性导出驱动路径,后续调用时可直接复用这些驱动。

我们还可以通过配置browserCapabilities属性来自定义浏览器的行为,比如设置失败时自动关闭浏览器、添加扩展程序、调整日志级别等。以下是一个完整的测试类示例,展示了如何设置浏览器相关的配置:

import com.codeborne.selenide.Selenide; import com.codeborne.selenide.Condition; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static com.codeborne.selenide.Selenide.*; import static com.codeborne.selenide.Condition.*; import io.github.bonigarcia.wdm.WebDriverManager; import org.openqa.selenium.chrome.ChromeOptions; public class LoginTest { // 假设BASE_URL为Angular应用的基础URL private static final String BASE_URL = "https://your-angular-app.com"; @BeforeAll static void setup() { // 初始化Chrome驱动 WebDriverManager.chromedriver().setup(); // 设置基础URL Configuration.baseUrl = BASE_URL; // 设置使用Chrome浏览器 Configuration.browser = "chrome"; // 测试结束后不关闭浏览器 Configuration.holdBrowserOpen = true; // 设置浏览器选项 Configuration.browserCapabilities = getBrowserOptions(); } // 获取Chrome浏览器选项 private static ChromeOptions getBrowserOptions() { ChromeOptions options = new ChromeOptions(); // 不使用无头模式 options.setHeadless(false); // 最大化窗口启动 options.addArguments("start-maximized"); // 禁用信息栏 options.addArguments("--disable-infobars"); // 禁用扩展程序 options.addArguments("--disable-extensions"); // 禁用GPU加速 options.addArguments("--disable-gpu"); // 禁用/dev/shm共享内存使用 options.addArguments("--disable-dev-shm-usage"); // 禁用沙盒模式 options.addArguments("--no-sandbox"); // 禁用进程内堆栈跟踪 options.addArguments("--disable-in-process-stack-traces"); // 禁用日志记录 options.addArguments("--disable-logging"); // 设置日志级别为3(仅记录错误) options.addArguments("--log-level=3"); // 允许所有来源的远程连接 options.addArguments("--remote-allow-origins=*"); return options; } @BeforeEach void openBrowser() { // 打开登录页面 open("https://your-angular-app.com/login"); } // 测试方法... } 

四、编写简单的JUnit 5 UI测试用例

下面是一个验证Angular登录页面的基础测试示例。该测试会等待登录页面完全加载,通过检测用户名和密码输入框来判断页面是否加载完成。页面加载完成后,在相应的输入框中输入登录凭证并提交表单。提交表单后,等待登录成功或失败的消息出现,并对消息进行验证。

import com.codeborne.selenide.Selenide; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.closeWebDriver; public class LoginTest { @Test void loginWithValidCredentials() { // 在用户名输入框中输入testuser $("#username").setValue("testuser"); // 在密码输入框中输入password $("#password").setValue("password"); // 点击登录按钮 $("#loginButton").click(); // 验证欢迎消息是否正确 $("#welcomeMessage").shouldHave(text("Welcome, testuser")); } @AfterEach void closeBrowser() { // 关闭浏览器驱动 Selenide.closeWebDriver(); } } 

五、处理元素查找失败的情况

Selenide默认的超时时间为30秒,这个时间通常比较充裕。这意味着在页面加载完成后,Selenide会等待30秒,看元素是否出现。如果元素在规定时间内没有出现(即不可见),Selenide会抛出NoSuchElementExceptionAssertionError异常。例如:

// 等待30秒,如果元素不存在则抛出AssertionError异常 $("#nonexistent").click(); 

我们可以使用断言来自定义这种行为。比如,使用shouldBeshouldHave方法来设置自定义的超时时间,时间一到就抛出异常,使测试方法失败。示例如下:

// 等待10秒,如果元素不可见则抛出TimeoutException异常 $("#nonexistent").shouldBe(visible, Duration.ofSeconds(10)); 

如果不想抛出异常,而是对找不到的元素进行其他处理,可以使用exists()isDisplayed()方法。使用这些方法时,Selenide不会抛出异常,而是返回truefalse。例如:

// 判断元素是否可见,返回false boolean isVisible = $("#nonexistent").isDisplayed(); 

六、查找和验证元素的重要方法

在使用Selenide进行测试时,以下这些方法可以帮助我们快速定位和验证浏览器中加载的网页里的HTML元素:

  • 根据ID或选择器查找元素$(“#elementId”)
  • 使用XPath查找元素$(By.xpath(“//div[@class=’example’]”))
  • 验证元素是否可见$(“.className”).shouldBe(visible)
  • 在输入框中设置值$(“#inputField”).setValue(“Test”)
  • 点击按钮$(“button”).click()
  • 验证元素是否包含特定文本$(“#message”).shouldHave(text(“Success”))
  • 验证复选框是否被选中$(“#checkbox”).shouldBe(checked)
  • 从下拉框中选择选项$(“#dropdown”).selectOption(“Option 1”)
  • 获取匹配选择器的元素数量$$(“.list-items”).size()
  • 查找并点击列表中的第三个元素$$(“.row”).get(2).click()
  • 等待元素消失$(“#loading”).should(disappear)
  • 检查元素是否存在且不抛出异常$(“#error”).exists()

七、生成测试报告

(一)默认报告

Selenide提供了内置的测试报告支持,它会自动生成截图和日志。要启用报告功能,需要配置Selenide进行截图并保存测试日志:

// 启用截图功能 Configuration.screenshots = true; // 保存失败测试的页面源代码 Configuration.savePageSource = true; 

测试执行完成后,Selenide会自动将以下文件存储在build/reports/teststarget/reports/tests目录下:

  • 截图:测试失败时自动捕获。
  • HTML源代码:保存失败测试的页面源代码。
  • 日志:浏览器控制台日志。

(二)使用Allure生成高级报告

Allure是一款功能强大的测试报告工具,结合Selenide使用,可以生成交互式的测试报告。通过Allure生成的报告,能直观展示测试执行历史、日志、截图以及失败原因等信息。

要在项目测试中集成Allure,首先需要添加其最新的依赖:

<dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-selenide</artifactId> <version>2.29.1</version> </dependency> 

然后,通过添加监听器来启用Allure报告:

import com.codeborne.selenide.SelenideLogger; import io.qameta.allure.selenide.AllureSelenide; import org.junit.jupiter.api.BeforeAll; public class LoginTest { @BeforeAll static void setUp() { // 其他设置代码 // 添加Allure监听器 SelenideLogger.addListener("AllureSelenide", new AllureSelenide()); } } 

测试运行完成后,可以在build/allure-results目录中查看生成的报告。如果想在浏览器中查看报告,执行以下命令:

allure serve build/allure-results 

八、总结

本教程虽简短,但内容实用,详细展示了如何有效地利用Selenium和Selenide进行Angular应用程序的UI测试。如果不使用Selenide,在测试过程中可能会因为加载屏幕和元素查找超时等问题而困扰。而借助Selenide,我们能够有效避免这些在Angular UI测试中常见的问题,提高测试效率和质量。希望大家通过学习,能在实际项目中熟练运用这些知识。