65.9K
CodeProject 正在变化。 阅读更多。
Home

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

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2023年12月11日

CPOL

14分钟阅读

viewsIcon

3501

本文讨论了企业在快速发布移动应用程序时面临的挑战,以及快速上市 (TTM) 的重要性。

随着数字革命的到来,如今每个企业的主要目标都是通过赋予最终客户更多权力来实现业务的数字化。随着数字化,移动应用程序市场蓬勃发展,每个企业都计划尽快向最终用户发布其移动应用程序。

更快的上市时间 (TTM) 已成为当今企业在日益激烈的竞争中生存的关键。然而,企业面临的主要挑战之一是发布周期时间,通常由于多种原因而延长。

以下是大多数测试人员在处理多个项目时遇到的导致移动应用程序发布延迟给最终客户的一些原因:

  • 测试的脆弱性
  • 编码问题
  • 构建生成耗时过长,导致自动化回归测试运行和手动探索性测试执行延迟。
  • 自动化测试 管道 比预期花费的时间更长,这最终无助于快速反馈。

这里可能还有其他原因。然而,我们列出了一些许多团队面临的常见问题。

鉴于上述原因,让我们来讨论一下自动化管道的运行时间比预期长,这最终无助于快速反馈 并探讨可能的解决方案。

考虑一个移动金融应用程序的场景,该应用程序帮助最终用户转账,并且是为 Android 和 iOS 平台构建的。有了这两个平台,运行自动回归测试套件对于从构建中获得快速反馈至关重要。

例如,每个测试套件都有 50 多个测试用例需要执行。因此,考虑到两个平台,我们需要运行 100 多个测试用例,之后才能获得关于构建是否按预期运行的反馈。

假设一个测试大约需要 10 秒才能运行,那么考虑到 50 个测试用例,运行单个 Android 自动化测试套件大约需要 500 秒,再运行 iOS 自动化测试套件需要另外 500 秒。因此,要在两个平台上运行测试,我们需要大约 1000 秒。

在这种情况下,并行测试可以帮助我们加快流程。

目录

什么是 Appium?

在深入了解并行测试和使用 Appium 编写移动自动化测试之前,让我们先了解一下 Appium。

Appium 是一个流行的移动应用程序测试框架。它是一个开源测试框架,支持多种编程语言,如 Java、JavaScript、C#、Python、PHP 和 Ruby。它有助于自动化 Android 和 iOS 平台的移动测试,并支持开箱即用地测试 React Native 应用程序。它可用于测试混合、原生和 Web 应用程序。查看博客 Web vs Hybrid vs Native Apps,了解更多关于原生、混合和 Web 应用程序的信息。

随着 Appium 2.0 版本的发布,框架发生了突破性的变化。Appium 2.0 是 Appium 五年来最重要的全新版本。Appium 2.0 所做的更改与任何特定平台的自动化行为无关;但是,它旨在将 Appium 转换为一个“平台”,在该平台中可以轻松创建和共享“驱动程序”和“插件”。查看博客 Appium 2 迁移指南,它将指导您了解 Appium 2 所做的所有更改。

在这篇博客中,我们将学习如何使用 Appium 和 Java 在 LambdaTest 云网格上(串行和并行)运行移动自动化测试。

那么,让我们开始吧!

设置 Appium 框架

在编写代码之前,让我们创建并设置项目。例如,编程语言、移动自动化框架、测试运行器等。在这篇博客中,我们将使用 Java 作为编程语言,Appium 作为测试移动应用程序的测试框架,TestNG 作为测试运行器,以及任何您选择的 IDE。

编程语言/工具/框架 版本
Java 17
Appium 2.0
Appium Java 客户端 9.0.0
TestNG 7.8.0
Maven 3.9.4

首先,我们需要在 IDE 中创建一个新的 Maven 项目。成功创建项目后,让我们在 pom.xml 文件中添加 Appium 和 TestNG 的依赖项。

文件名:pom.xml
<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>appium-lambdatest-demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>appium-lambdatest-demo</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <appium-java-client-version>9.0.0</appium-java-client-version>
    <testng.version>7.8.0</testng.version>
    <lombok-version>1.18.30</lombok-version>
    <maven.compiler.version>3.11.0</maven.compiler.version>
    <java.release.version>17</java.release.version>
    <maven.surefire.version>3.2.2</maven.surefire.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>
    <!-- https://mvnrepository.com/artifact/io.appium/java-client -->
    <dependency>
      <groupId>io.appium</groupId>
      <artifactId>java-client</artifactId>
      <version>${appium-java-client-version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.testng/testng -->
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>${testng.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok-version}</version>
      <scope>provided</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>${maven.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 文件中的依赖项已更新。

使用 Appium 和 Java 编写第一个测试用例

在编写移动应用程序的自动化测试之前,我们必须讨论要测试的应用程序以及我们将自动化的场景。

LambdaTest 的以下移动应用程序,它是使用 react-native 构建的,将在本博客中使用,通过 Appium 编写自动化测试。

Proverbial 移动应用程序的主页

以下测试场景将用于在 Android Proverbial App 上执行移动自动化测试。

测试场景 1

  1. 在 LambdaTest Cloud 网格上的 Android 真机上打开 Proverbial 应用程序。
  2. 点击 Text 按钮,并检查是否显示文本 Proverbial

测试场景 2

  1. 在 LambdaTest Cloud 网格上的 Android 真机上打开 Proverbial 应用程序。
  2. 点击 Notification 按钮,并检查 Notification 是否显示在顶部。
  3. 通过点击通知来关闭它。

测试场景 3

  1. 在 LambdaTest Cloud 网格上的 Android 真机上打开 Proverbial 应用程序。
  2. 点击 Toast 按钮,检查 Toast 消息是否显示在屏幕底部,并验证其文本:“Toast should be visible”。

配置设置

正如前面关于设置框架的部分所述,我们已经创建了 Maven 项目。项目结构的以下屏幕截图

我们将在 LamdaTest Cloud 网格上的以下 Android 真机上运行测试。

设备 # 平台名称 平台版本 设备名称
1 Android 12 三星 Galaxy S22 5G
2 Android 13 谷歌 Pixel 7

需要注意的是,移动应用程序需要使用 LambdaTest API 导入到 LambdaTest Cloud 中进行移动应用测试。响应中收到的 app_id 可以用作测试中“app”期望功能的值。

首先,让我们创建一个用于管理 Android Driver 的新类,并将其命名为 AndroidDriverManager。此类将帮助我们设置 AndroidDriver、用于在 LambdaTest Cloud 网格上运行测试的期望功能(以在不同设备、平台版本等上运行测试),以及 Android Driver 的超时。

@Builder
public class AndroidDriverManager {

    private String buildName;
    private String testName;
    private Platform platform;
    private String platformVersion;
    private String deviceName;
    private String app;


    private static final ThreadLocal<AndroidDriver> DRIVER = new ThreadLocal<>();
    private static final String LT_USERNAME = System.getProperty("LT_USERNAME");
    private static final String LT_ACCESS_KEY = System.getProperty("LT_ACCESS_KEY");
    private static final String GRID_URL = "@mobile-hub.lambdatest.com/wd/hub";


    public AndroidDriverManager createAndroidDriver() {
        try {
            setDriver(new AndroidDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_KEY, GRID_URL)), setCapabilities()));
            setupBrowserTimeouts();

        } catch (MalformedURLException e) {
            throw new Error("Error while creating Android Driver Session");
        }
        return this;
    }

    public AndroidDriver getAndroidDriver() {
        return AndroidDriverManager.DRIVER.get();
    }

    public void quitDriver() {
        if (null != DRIVER.get()) {
            getAndroidDriver().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_KEY);
        ltOptions.put("platformName", platform);
        ltOptions.put("deviceName", deviceName);
        ltOptions.put("platformVersion", platformVersion);
        ltOptions.put("app", app);
        ltOptions.put("build", buildName);
        ltOptions.put("name", testName);
        ltOptions.put("w3c", true);
        ltOptions.put("isRealMobile", true);
        ltOptions.put("autoGrantPermissions", true);
        ltOptions.put("plugin", "java-testNG");
        ltOptions.put("visual", true);
        ltOptions.put("console", true);
        ltOptions.put("devicelog", true);
        return ltOptions;
    }

    private DesiredCapabilities setCapabilities() {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability("lt:options", ltOptions());
        return capabilities;
    }

    private void setDriver(final AndroidDriver driver) {
        AndroidDriverManager.DRIVER.set(driver);
    }

    private void setupBrowserTimeouts() {
        getAndroidDriver().manage()
                .timeouts()
                .implicitlyWait(Duration.ofSeconds(20));
    }
}

代码演练

让我们逐步了解 AndroidDriverManager 类中的代码,因为它是设置自动化测试运行配置的主要类。

步骤 1ThreadLocal 类用于实例化 Android Driver 实例。它确保每个线程都有一个独立的 Android Driver 实例,这将有助于并行运行测试。

步骤 2ThreadLocal 的主要优点是它有助于安全的驱动程序设置,尤其是在并行运行测试时。它隔离了不同的线程,即使它们在同一个 ThreadLocal 对象上设置不同的值,也能提供线程安全。

步骤 3:这种隔离有助于线程独立运行,并防止线程覆盖相关的问题和冲突。

在 LambdaTest 平台上运行测试必须设置以下强制性功能。LambdaTest Capabilities Generator 可以配置这些功能,确保您的测试与 LambdaTest 平台无缝协作。

功能名称 描述
GRID_URL 在 LambdaTest Cloud 网格上运行测试所需的远程 URL
LT_USERNAME LambdaTest 用户名
LT_ACCESS_KEY LambdaTest 访问密钥

LambdaTest Capabilities Generator 允许通过从 UI 中轻松选择所需配置来设置功能。相应地,它会生成可以轻松复制粘贴到项目中的代码,以无缝启动移动自动化测试。

在 AndroidDriverManager 类中设置功能

使用 LambdaTest Capabilities Generator 生成的功能已在 createAndroidDriver() 方法中使用的 ltOptions() 方法中更新,用于在 LambdaTest Cloud 网格上为 Android Driver 设置功能。这将允许我们在真实的 Android 设备上运行测试。

变量 platform、platformversion、devicename、app 和 build name 的值将在运行时通过 testng.xml 文件提供。

Appium 的 AndroidDriver() 方法将帮助我们在 LambdaTest Cloud 网格上实例化一个新的 Android Driver 会话。它将通过传递 GRID_URLLT_USERNAMELT_ACCESS_KEY 和期望的功能来创建。

成功创建 Android Driver 会话后,将使用 setupBrowserTimeouts() 方法设置 20 秒的隐式等待超时。

添加隐式等待,以便驱动程序在找到元素或超时到期之前轮询页面。它适用于屏幕上的所有元素。

同样,可以添加显式等待来查找或定位元素。它使用 ExpectedConditions 类实现,并使用适当的显式条件等待元素。

getDriver() 方法将返回 Android Driver 的当前实例。这将在 Page Object 类和测试中进一步使用。

最后,创建了 quitDriver() 方法以优雅地退出驱动程序。

至此,所有配置都已设置完毕,让我们继续编写本文前面讨论的测试场景的自动化测试。

配置测试

需要创建一个 BaseTest 类,其中包含所有通用的配置设置,例如启动和停止驱动程序以及设置期望的功能,如平台、平台版本、设备名称、构建名称、测试名称等,这些是运行测试所必需的。

这个 BaseTest 类将被实际的 Test 类继承。

文件名:BaseTest.java
public class BaseTest {

    protected AndroidDriverManager androidDriverManager;

    @Parameters({"buildName", "testName", "app", "platformName", "version", "device"})
    @BeforeClass(alwaysRun = true)
    public void setupTest(final String buildName, final String testName, @Optional("app") final String app, final Platform platform, final String platformVersion,
                          final String deviceName) {
        this.androidDriverManager = AndroidDriverManager.builder()
                .buildName(buildName)
                .testName(testName)
                .app(app)
                .platform(platform)
                .platformVersion(platformVersion)
                .deviceName(deviceName)
                .build()
                .createAndroidDriver();
    }

    @AfterSuite(alwaysRun = true)
    public void tearDown() {
        this.androidDriverManager.quitDriver();
    }
}

TestNG 中的 @Parameters 注释有助于通过 testng.xml 文件配置多个参数。它在此 BaseTest 类中使用,以便可以动态更新不同的参数值,如平台、平台版本、设备名称、测试名称、构建名称、App ID 等,而无需修改任何代码,并可以相应地执行测试。

测试场景实现

我们将编写博客中讨论的 3 个测试场景的测试。创建了 AndroidTests 类来包含所有测试场景。它继承了 BaseTest 类,因此我们可以轻松使用 AndroidDriverManager 类的实例。

文件名:AndroidTests
public class AndroidTests extends BaseTest {

    private HomePage homePage;

    @BeforeClass
    public void setupTest() {
        this.homePage = new HomePage(this.androidDriverManager);
    }

    @Test
    public void textTests() {
        assertEquals(this.homePage.getText(), "Hello! Welcome to lambdatest Sample App called Proverbial");
        this.homePage.tapOnTextBtn();
        assertEquals(this.homePage.getText(), "Proverbial");
    }

    @Test
    public void notificationTest() {
        this.homePage.tapOnNotificationBtn();
        assertTrue(this.homePage.notificationBar().isDisplayed());
        this.homePage.notificationBar().click();
    }

    @Test
    public void toastMessageTest() {
        this.homePage.tapOnToastBtn();
        assertEquals(this.homePage.toastMessage(), "Toast should be visible");
    }
}

textTests() 方法包含第一个测试场景的自动化验证代码,在该场景中,我们检查消息文本 - Hello! Welcome to lambdatest Sample App called Proverbial在应用程序成功启动后显示在其主页上。接下来,验证点击“Text”按钮时文本“Proverbial”是否正确显示。

notificationTest() 方法验证第二个测试场景,即点击“Notification”按钮后通知是否显示。显示通知后,点击通知栏将其关闭。

toastMessageTest() 方法检查点击“Toast”按钮后 Toast should be visible的 toast 消息是否显示。

您会注意到 HomePage 类已被全局声明。这样做是因为它在每个测试中使用,并在任何测试运行之前运行的 setupTest() 方法中实例化。因此,我们在调用任何测试之前都有 HomePage 类的对象。这消除了在每个测试中实例化 HomePage 类的代码重复。

Page Object Model 提供了代码可重用性的灵活性,并减少了代码重复。它还使代码易于维护。因此,它在许多软件测试自动化项目中得到广泛应用。

HomePage 类是 Page Object 类,其中包含移动应用程序主页上的所有元素。

public class HomePage {

    AndroidDriverManager androidDriverManager;
    WebDriverWait wait;

    public HomePage(final AndroidDriverManager androidDriverManager) {
        this.androidDriverManager = androidDriverManager;
        wait = new WebDriverWait(androidDriverManager.getAndroidDriver(), Duration.ofSeconds(20));
    }

    private WebElement textBtn() {
        return androidDriverManager.getAndroidDriver()
                .findElement(AppiumBy.id("Text"));
    }

    public void tapOnTextBtn() {
        textBtn().click();
    }

    public String getText() {
        return androidDriverManager.getAndroidDriver()
                .findElement(AppiumBy.id("Textbox"))
                .getText();
    }

    private WebElement notificationBtn() {
        return androidDriverManager.getAndroidDriver()
                .findElement(AppiumBy.id("notification"));
    }

    public void tapOnNotificationBtn() {
        notificationBtn().click();
    }

    public WebElement notificationBar() {
        return androidDriverManager.getAndroidDriver()
                .findElement(AppiumBy.id("action_bar"));
    }

    private WebElement toastBtn() {
        return androidDriverManager.getAndroidDriver()
                .findElement(AppiumBy.id("toast"));
    }

    public void tapOnToastBtn() {
        toastBtn().click();
    }

    public String toastMessage() {
        return wait.until(ExpectedConditions.presenceOfElementLocated(AppiumBy.xpath("//android.widget.Toast[1]")))
                .getText();
    }
}

可以使用 Appium Inspector 工具来定位移动元素。它是 Appium 生态系统的一部分,提供了一种无忧无虑的方式来查找和定位原生和混合应用程序的所有移动元素。

可以通过从 GitHub 下载并安装 Appium Inspector 工具来本地使用它。接下来,我们需要在本地启动 Appium 服务器。要检查移动元素,我们必须提供带有应用程序路径的期望功能。

可以在终端中执行以下命令以在本地启动 Appium 服务器

appium server --use-drivers uiautomator2

Appium 服务器成功启动后,应在终端中显示以下日志

接下来,启动 Appium Inspector 并提供期望的功能。必须提供 Remote host、Remote Port 和 Remote Path 以将 Appium Inspector 与本地启动的 Appium Server 连接。

我们还需要一个本地设备来启动 Android 应用程序并检查元素。因此,我们将在此使用 Android 模拟器。可以使用 Android Studio 应用程序启动 Android 模拟器。我创建了一个名为“PIXEL XL API 33”的 Android 模拟器,将在演示中使用它来检查 Proverbial 应用程序的移动元素。

在 Appium Inspector 中必须提供以下期望功能才能启动应用程序并检查其元素。

点击 Start Session 按钮开始检查移动元素。

我们将定位的第一个元素是消息文本 - Hello! Welcome to lambdatest Sample App called Proverbial显示在应用程序的主页上。

可以看到,消息文本的定位策略是 id,选择器为 “com.lambdatest.proverbial:id/Textbox”。它在 HomePage 类中使用 getText() 方法返回元素的文本。

接下来,定位 Text 按钮。点击 Appium Inspector 中的按钮并检查显示的选择器。

Text 按钮的选择器是 “com.lambdatest.proverbial:id/Text”,使用 id 定位策略。它在 HomePage 类中的 textBtn() 方法中使用。

接下来,我们需要定位 Notification 按钮和 Notification bar 以验证第二个场景,我们需要点击它。

Notification 按钮的选择器 - “com.lambdatest.proverbial:id/notification”,使用 id 定位策略。它在 HomePage 类中的 notificationBtn() 方法中使用。

接下来,让我们定位 Notification bar,以便在显示后点击它来关闭它。

Notification bar 的选择器 - “com.lambdatest.proverbial:id/action_bar”,使用 id 定位策略。它在 HomePage 类的 notificationBar() 方法中使用。

需要定位 Toast 按钮以点击它来自动化第三个场景。

可以看到,Toast 按钮的选择器 - “com.lambdatest.proverbial:id/toast”,使用 id 策略。它在 toastBtn() 方法中使用以定位元素。

最后,为了定位 toast 消息,在 toastMessage() 方法中使用了具有 Xpath 的选择器 - “//android.widget.Toast[1]”,该方法以 String 格式返回元素的文本。

使用 Appium 在云网格上大规模运行自动化测试

以下 testng.xml 文件将并行运行测试。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Lambda tests Mobile automation test suite"  parallel="tests">
    <test name="Android Proverbial app tests on Samsung S22 5G">
        <parameter name="buildName" value="Android Proverbial app Build"/>
        <parameter name="testName" value="Proverbial android app tests on Samsung S22 5G"/>
        <parameter name="app" value="lt://APP10160252091701161593570567"/>
        <parameter name="platformName" value="ANDROID"/>
        <parameter name="version" value="12"/>
        <parameter name="device" value="Galaxy S22 5G"/>
        <classes>
            <class name="io.github.mfaisalkhatri.tests.AndroidTests">
                <methods>
                    <include name="textTests"/>
                    <include name="notificationTest"/>
                    <include name="toastMessageTest"/>
                </methods>
            </class>
        </classes>
    </test> <!-- Test -->
    <test name="Android Proverbial app tests on Google Pixel 7">
        <parameter name="buildName" value="Android Proverbial app Build"/>
        <parameter name="testName" value="Proverbial android app tests on Pixel 7"/>
        <parameter name="app" value="lt://APP10160252091701161593570567"/>
        <parameter name="platformName" value="ANDROID"/>
        <parameter name="version" value="13"/>
        <parameter name="device" value="Pixel 7"/>
        <classes>
            <class name="io.github.mfaisalkhatri.tests.AndroidTests">
                <methods>
                    <include name="textTests"/>
                    <include name="notificationTest"/>
                    <include name="toastMessageTest"/>
                </methods>
            </class>
        </classes>
    </test> <!-- Test -->
</suite> <!-- Suite -->

可以看到,testng.xml 文件中总共有 2 个测试块。这两个测试都将并行运行。请注意,在提供套件名称的行中提供了 parallel="tests"。这有助于节省测试执行时间,因为所有测试都是并发执行的。

第一个测试块将在三星 S22 5G 真机上执行 Android 12 的测试,第二个块将在谷歌 Pixel 7 真机上执行 Android 13 的测试。

由于我们在代码中结合使用了 ThreadLocal 类,因此无需担心 Android Driver 会话重叠,因为它具有线程安全性。

使用 IntelliJ IDE 执行的测试截图

LambdaTest Dashboard 提供了对在 LambdaTest Cloud 网格上执行的测试的更好洞察。它在 LambdaTest Analytics 中提供有关测试执行的分步详细信息。它提供带有视频录制、设备日志、屏幕截图等的细粒度详细信息,以提高测试执行的可视性。

LambdaTest Dashboard

结论

Appium 是一个流行的移动自动化框架,它允许我们自动化 Android 和 iOS 应用程序测试。测试自动化通过快速运行所有回归和功能测试为我们提供更快的反馈。

通过将移动自动化测试与 LambdaTest 等云提供商集成,我们可以在不同平台、平台版本和真实设备上并行和大规模地运行移动自动化测试。它提供按需的真实设备来测试我们的 Android 和 iOS 应用程序。

LambdaTest Analytics 为我们提供了对测试执行的公正可见性,其中包含详细信息,可以帮助测试自动化工程师向其软件工程团队和利益相关者创建详细的自动化测试执行报告。

© . All rights reserved.