移动应用自动化测试集成框架

项目地址直达,但是还是希望大家能够看完下面内容再去,非常有利于之后使用该框架,而且也写了好长时间...

简介

日常工作中,经常需要给不同的移动应用开发自动化代码。为了方便自己和其他有自动化需求的小伙伴,这里将平常我经常使用的一套自动化框架整合并放出来,希望可以帮助大家。

框架中集成了以下工具:

  1. Appium 使用 Appium 作为自动化引擎,支持 Android,iOS 和 PC Bowser,代码中也写了两个例子供大家参考。
  2. TestNG 集成 TestNG 来管理测试用例。
  3. ReportNG 和 ExtentReports 这里集成了两个 Report 工具,相比 TestNG 自带的测试报告,这两个工具均可以提供非常简单美观的测试报告。 为什么要集成两个,也是为了不同的需求,ReportNG 已经停止维护了,但是我们之前的很多项目中还是使用 ReportNG,而且已经和 Jenkins 进行了深度整合,所以为了保证兼容性,还是集成了 ReportNG。 ExtentReports 是一个比较好的测试报告的代替品,但是需要自己写监听器来初始化和自定义报表,框架中已经定义好了,可以直接使用。 两个报表工具均已经配置好,开箱可用,可以自由选择哪个报表工具作为最终展现。
  4. Log4j2 + SLF4j 集成 Log4j2 和 SLF4j 作为日志处理工具。
  5. Lombok 集成 Lombok 简化代码。
  6. Maven 集成 Maven 和 Maven Wrapper。已经在 POM 文件中配置好了,可以在命令行直接运行mvn test来跑 TestNG 定义的测试用例。当然了,你依旧可以通过直接运行 TestNG 的 XML 文件来跑。集成 Maven 的优点是方便大家做持续集成。

功能

Appium 初始化

项目中已经集成了 Appium 最新的客户端,初始化 Appium 客户端的代码也已经完成,只需要提供必要的 Capabilities 即可。

初始化 Appium client driver

初始化代码请参考:InitialTestSuite.java

public void initialDriver(@Optional("http://127.0.0.1:4723/wd/hub") String appiumRemoteURL,
                              String deviceName,
                              @Optional("Android") String platformName,
                              String platformVersion,
                              @Optional("UIAutomator2") String automationName,
                              String appName,
                              String appPackage,
                              String appActivity) throws MalformedURLException {

        DesiredCapabilities desiredCapabilities = null;

        if (platformName.equalsIgnoreCase("Android")) {
            desiredCapabilities = initialAndroidDriver(deviceName, platformName, platformVersion, automationName, appName, appPackage, appActivity);
        } else if (platformName.equalsIgnoreCase("iOS")) {
            desiredCapabilities = initialIOSDriver(deviceName, platformName, platformVersion, automationName, appName);
        } else {
            log.error("For \"platform\" parameter, only support \"Android\" or \"iOS\"");
            throw new IllegalArgumentException("For \"platform\" parameter, only support \"Android\" or \"iOS\"");
        }

        TestCommonSuite.driver = new AndroidDriver(new URL(appiumRemoteURL), desiredCapabilities);

        TestCommonSuite.driver.manage().timeouts().implicitlyWait(
                Integer.parseInt(resourceBundle.getString("appium.driver.implicitlyWait")),
                TimeUnit.SECONDS);
    }
如何使用

Appium client driver 定义在 TestCommonSuite.java 的抽象类中,在自动化代码运行之前,会用上述代码进行初始化,如果想要写自己的测试用例,就需要集成该抽象类,这样就可以你自己的类中直接使用driver了。

抽象类代码如下:

public abstract class TestCommonSuite {
    public static AppiumDriver driver = null;
}

继承该抽象类即可使用 driver:

public class TestClass extends TestCommonSuite {
    public void testMethod() throws InterruptedException {
        driver.findElementById("testID").click();
    }
}

更多 Appium 知识请参考官网

TestNG 测试用例管理

集成 TestNG 来管理测试用例,xml 文件放置在 resources 目录下。

创建新的 TestNG 文件方法

一、创建空 XML,加入以下代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
    <test verbose="2" preserve-order="true" name="E:/6CodeRepository/AutomationTools/E2EAT/src/main/resources">
    </test>
</suite>

注意:<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">这一行代码一定要加上,声明该文件为 TestNG 文件,并提供了代码提示和自动完成功能。

二、使用Create TestNG XML插件

Ctrl+ALT+S 打开设置,选择 Plugins,搜索Create TestNG XML插件并安装,之后就可以在右键菜单上找到Create TestNG XML选项,使用该选项可以直接在选定目录下生成testng.xml

注意:所有的自动化代码请放置在 Maven 工程的 Test 目录下

初始化 Appium Driver

初始化 Appium Driver 所需要的参数都放在 testng.xml 文件里,示例:

<parameter name="appiumRemoteURL" value="http://127.0.0.1:4723/wd/hub"/>

<!-- Modify parameters according to your needs -->
<parameter name="deviceName" value="Android"/>
<parameter name="platformName" value="Android"/>
<parameter name="platformVersion" value="10.0"/>
<parameter name="automationName" value="UIAutomator2"/>

<!-- App name, not path, put your apk file under “resources/apks” folder. -->
<parameter name="appName" value=""/>
<!--
        Android Only
        If there is "appName", will ignore the "appPackage" and "appActivity",
          otherwise, will use "appPackage" and "appActivity"
    -->
<parameter name="appPackage" value="com.google.android.calculator"/>
<parameter name="appActivity" value="com.android.calculator2.Calculator"/>

需要注意,如果要在 Android 设备上运行自动化代码,那么appappPackageappActivity二者取其一即可,摘抄 Appium 官网说明:

The absolute local path or remote http URL to a .ipa file (IOS), .app folder (IOS Simulator), .apk file (Android) or .apks file (Android App Bundle), or a .zip file containing one of these. Appium will attempt to install this app binary on the appropriate device first. Note that this capability is not required for Android if you specify appPackage and appActivity capabilities (see below). UiAutomator2 and XCUITest allow to start the session without app or appPackage. Incompatible with browserName. See here about .apks file.

更多 TestNG 知识请参考官网

日志功能

使用 SLF4j + Log4j2 作为日志管理工具,Log4j2 的配置文件放在 resources 文件下,摘抄如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Status:  TRACE < DEBUG < INFO < WARN < ERROR < FATAL
    Log level defined in <logger> will override this configuration.
    monitorInterval: Read the configuration file every 300 s.
-->
<Configuration status="WARN" monitorInterval="300">
    <properties>
        <property name="LOG_HOME">./logs</property>
        <property name="FILE_NAME">E2EATLog</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <RollingFile name="RollingFileCommonMSG"
                     fileName="${LOG_HOME}/${FILE_NAME}.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log">
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="6"/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Logger name="com.automation" level="DEBUG" additivity="false" includeLocation="true">
            <AppenderRef ref="RollingFileCommonMSG"/>
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="org.testng" level="DEBUG" additivity="false" includeLocation="true">
            <AppenderRef ref="RollingFileCommonMSG"/>
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="com.github.appium" level="DEBUG" additivity="false" includeLocation="true">
            <AppenderRef ref="RollingFileCommonMSG"/>
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

properties中可以定义 log 文件存放目录和文件名称,这里我使用的是项目根目录下的 logs 目录,可以根据时间和大小进行分卷并归类,如下图:

你可以自定义该文件来满足你的需求。

测试报告

如上文所说,集成了 ReportNG 和 Extent Reports 两个报表工具。

ReportNG

ReportNG 依赖导入后不需要自定义,在 testng.xml 文件中加入一下代码后即可直接使用:

<listeners>
    <listener class-name="org.uncommons.reportng.HTMLReporter"/>
    <listener class-name="org.uncommons.reportng.JUnitXMLReporter"/>
</listeners>

生成的报表下图:

Extent Reports

Extent Reports 依赖导入以后,可以使用监听器来自定义报表属性。项目中自定义代码参考:ExtentReportListener.java,可以参考该代码来自定义你的 Extent Reports。

同样的,我们也需要在 testng.xml 文件中加入以下代码才可使用:

<listeners>
    <listener class-name="com.automation.e2e.ExtentReportListener"/>
</listeners>

生成的报表如下图:

Maven

我们有两种方式来运行 testng.xml,一种是直接右键该文件,选择run...,另一种是使用 Maven 来运行测试。

运行 testng.xml

右键 testng.xml 文件,选择Run ...来运行

Maven

使用surefire来将 TestNG 测试文件绑定到 Maven Test。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.20.1</version>
    <configuration>
        <testFailureIgnore>true</testFailureIgnore>
        <suiteXmlFiles>
            <!-- Location of TestNG xml -->
            <suiteXmlFile>${testngFileName}</suiteXmlFile>
        </suiteXmlFiles>
        <properties>
            <property>
                <name>suitethreadpoolsize</name>
                <value>2</value>
            </property>
            <!-- Use ReportNG to instead of the default report of TestNG -->
            <property>
                <name>usedefaultlisteners</name>
                <value>false</value>
            </property>
            <!-- listener -->
            <property>
                <name>listener</name>
                <value>org.uncommons.reportng.HTMLReporter, org.uncommons.reportng.JUnitXMLReporter,
                    com.automation.e2e.AroundLoggerListener,
                    com.automation.e2e.ExtentReportListener
                </value>
            </property>
        </properties>
        <argLine>-Dfile.encoding=UTF-8</argLine>
        <argLine>-Dtestng.dtd.http=true</argLine>
    </configuration>
</plugin>

这样就可以直接在命令行使用mvn test来运行自动化测试,如 GIF 所示:

自定义监听器

正如前面所说,我们在使用 Extent Reports 的过程中是需要使用自定义监听器的。

TestNG 的自定义监听器非常简单,写好了你的监听器文件之后,在 XML 文件的<listeners></listeners>中加入你的自定义监听器即可。

项目中加入了两个自定义监听器,均可以在项目 src\main 目录下找到,一个是 Extent Reports 的自定义监听器,另一个是 Log 的自定义监听器。

常用工具箱

项目中还加入了常用的工具来方便大家开发,如:

  1. 切换到 WebView context

  2. 切换到 Native context

  3. 向上/向下滚动屏幕

  4. 滚动屏幕到指定元素(WebView context)

  5. WebView 下模拟 Native Tap 方法(Android & iOS)

  6. 截图

  7. 等待元素(WebView context)

这个是非常常用的方法,在写自动化代码的时候经常用到,为显示等待,实现了三种显示等待:

  1. wait for element present
  2. wait for element visible
  3. wait for element clickable

所有的这些实用方法均放在AppiumUtils.java类中。该类为线程安全的懒加载单例模式,可放心在各种环境下使用,使用方法为:

AppiumUtils appiumUtils = AppiumUtils.getAppiumUtils();

目录结构

实例

项目中集成了两个例子,一个是 Android Native App 的自动化,一个是 Hybrid App 的自动化。

Android Native App: Calculator

这里使用安卓自带的计算器来作为 Native 的示例

用例描述
  1. 打开安卓自带的计算器
  2. 输入:1 + 2
  3. 输出:3
Capabilities

Android 10 原生系统中不带计算器,需要去 Google Play 下载。

<parameter name="appiumRemoteURL" value="http://127.0.0.1:4723/wd/hub"/>

<!-- Modify parameters according to your needs -->
<parameter name="deviceName" value="Android"/>
<parameter name="platformName" value="Android"/>
<parameter name="platformVersion" value="10.0"/>
<parameter name="automationName" value="UIAutomator2"/>

<!-- App name, not path, put your apk file under “resources/apks” folder. -->
<parameter name="appName" value=""/>
<!--
        Android Only
        If there is "appName", will ignore the "appPackage" and "appActivity",
          otherwise, will use "appPackage" and "appActivity"
    -->
<parameter name="appPackage" value="com.google.android.calculator"/>
<parameter name="appActivity" value="com.android.calculator2.Calculator"/>
运行效果

Hybrid App: Chrome

这里我们选用 Chrome 来作为 Hybrid App 的示例。

用例描述
  1. 打开 Chrome 浏览器
  2. 跳过配置
  3. 访问 Baidu.com
  4. 输入 Appium 到搜索框并搜索
  5. 展示搜索结果

其中 1,2,3 步骤在 native context 中完成,之后需要切换到 WebView context 中才能继续操作,切换 context 的代码使用的正是上文所提到的AppiumUtils.java中的切换代码。

capabilities
<parameter name="appiumRemoteURL" value="http://127.0.0.1:4723/wd/hub"/>

<!-- Modify parameters according to your needs -->
<parameter name="deviceName" value="Android"/>
<parameter name="platformName" value="Android"/>
<parameter name="platformVersion" value="10.0"/>
<parameter name="automationName" value="UIAutomator2"/>

<!-- App name, not path, put your apk file under “resources/apks” folder. -->
<parameter name="appName" value=""/>
<!--
        Android Only
        If there is "appName", will ignore the "appPackage" and "appActivity",
          otherwise, will use "appPackage" and "appActivity"
    -->
<parameter name="appPackage" value="com.android.chrome"/>
<parameter name="appActivity" value="com.google.android.apps.chrome.Main"/>
运行效果

总结

这套框架目前我也在使用,依旧在不断完善,这里开源出来,欢迎大家使用并提意见,当然也欢迎大家多多支持。


↙↙↙阅读原文可查看相关链接,并与作者交流