App 自动化测试测试实战

霍格沃兹测试开发

ceshiren.com

目录

  • 知识点梳理
  • 测试流程
  • 知识点练习
    • 业务分析
    • 用例设计
    • 编写用例
  • 练习题点评与解析

App 测试的时代背景

  • 按月发布->按周发布->按小时发布
  • 多端发布: Android、iOS、微信小程序、h5
  • 多环境发布: 联调环境、测试环境、预发布环境、线上环境
  • 多机型发布: 众多设备型号、众多系统版本
  • 多版本共存: 用户群体中存在多个不同的版本
  • 历史回归测试任务: 成百上千条业务用例如何回归

总结:加班 + 背锅

UI 自动化测试需要哪些技术

  • App 自动化测试: Appium、Airtest、ATX 等

Appium 介绍

  • 官网: appium.io
  • 跨语言 Java、Python、nodejs 等
  • 跨平台
    • Andoid、iOS
    • Windows、Mac
  • 底层多引擎可切换
  • 生态丰富,社区强大

Appium 知识点梳理

功能测试场景

UI 自动化测试场景

企业微信 app 项目实战

环境准备

  • MacOS 系统
  • Android 手机(网易 mumu)
  • Appium Server、Appium Client

企业微信实战(添加成员功能)

  • 前提条件:

    • 1、提前注册企业微信管理员帐号
    • 2、手机端安装企业微信
    • 3、企业微信 app 处于登录状态

企业微信实战(添加成员功能)

  • 通讯录添加成员用例步骤

    • 打开【企业微信】应用
    • 进入【通讯录】页面
    • 点击【添加成员】
    • 点击【手动输入添加】
    • 输入【姓名】【手机号】并点击【保存】

用例步骤流程图

用例验证流程图

app启动参数获取

  • 安卓系统版本号获取

    #获取手机系统版本
    adb shell getprop ro.build.version.release
    
  • 获取企业微信的app包名

    #获取第三方包名
    adb shell pm list packages -3
    
  • 获取企业微信的appActivity页面

    #获取打开app的首页
    adb shell monkey -p  <package_name> -vvv 1
    

pom.xml


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <!-- 使用 Java 11 语言特性 ( -source 11 ) 并且还希望编译后的类与 JVM 11 ( -target 11 )兼容,您可以添加以下两个属性,它们是默认属性插件参数的名称-->
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.source>11</maven.compiler.source>
        <!-- 对应junit Jupiter的版本号;放在这里就不需要在每个依赖里面写版本号,导致对应版本号会冲突-->
        <junit.jupiter.version>5.8.2</junit.jupiter.version>
        <!-- plugins -->
        <maven.compiler.version>3.8.1</maven.compiler.version>
        <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
        <!-- 断言-->
        <hamcrest.version>2.2</hamcrest.version>
        <!--log日志-->
        <slf4j.version>2.0.0-alpha7</slf4j.version>
        <logback.version>1.3.0-alpha16</logback.version>
        <!--allure报告-->
        <allure.version>2.18.1</allure.version>
        <allure-maven.version>2.10.0</allure-maven.version>
        <allure.cmd.download.url>
            https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline
        </allure.cmd.download.url>
        <aspectj.version>1.9.9.1</aspectj.version>
        <!-- faker -->
        <javafaker.version>1.0.2</javafaker.version>
        <!-- selenium -->
        <selenium.version>4.3.0</selenium.version>
        <!-- appium -->
        <appium.version>8.1.1</appium.version>
        <jackson.version>2.13.1</jackson.version>
        <commonio.version>2.11.0</commonio.version>
    </properties>
    <!--    物料清单 (BOM)-->
    <dependencyManagement>
        <dependencies>
            <!--当使用 Gradle 或 Maven 引用多个 JUnit 工件时,此物料清单 POM 可用于简化依赖项管理。不再需要在添加依赖时设置版本-->
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>${junit.jupiter.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>31.0.1-jre</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </repository>
    </repositories>

    <dependencies>
        <!--junit5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <!--对应添加的依赖的作用范围-->
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <!--hamcrest断言-->
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest</artifactId>
            <version>${hamcrest.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>${hamcrest.version}</version>
            <scope>test</scope>
        </dependency>
        <!--faster json解析yaml文件 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
            <version>${jackson.version}</version>
            <scope>compile</scope>
        </dependency>
        <!-- log日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- common io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commonio.version}</version>
        </dependency>
        <!-- allure-->
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit5</artifactId>
            <version>${allure.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!--  随机测试数据生成库  -->
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>${javafaker.version}</version>
        </dependency>
        <!-- selenium-->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>
        <!-- appium -->
        <dependency>
            <groupId>io.appium</groupId>
            <artifactId>java-client</artifactId>
            <version>${appium.version}</version>
            <scope>compile</scope>
        </dependency>


    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                    <includes>
                        <include>**/*Test</include>
                    </includes>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.junit.jupiter</groupId>
                        <artifactId>junit-jupiter-engine</artifactId>
                        <version>${junit.jupiter.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                        <version>${junit.jupiter.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.version}</version>
                <configuration>
                    <parameters>true</parameters>
                    <!-- 设置jre版本为 11 -->
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <!-- 设置编码为 UTF-8 -->
                    <encoding>${maven.compiler.encoding}</encoding>
                </configuration>
            </plugin>

            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>${allure-maven.version}</version>
                <configuration>
                    <reportVersion>${allure.version}</reportVersion>
                    <allureDownloadUrl>${allure.cmd.download.url}/${allure.version}/allure-commandline-${allure.version}.zip</allureDownloadUrl>
                </configuration>
            </plugin>
        </plugins>
    </build>

总结

  • JUnit5 用法
  • DesireCapbility的配置
  • 元素定位(id,xpath等)
  • 交互:click(),sendKeys()
  • 等待方式:隐式等待

优化

  • toast元素验证
  • 添加log日志
  • 添加截图
  • 添加allure报告

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!-- 默认为 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
        <pattern>%date %-5level %logger{36}.%M\(%line\) -- %msg%n</pattern>
    </encoder>

    <!-- name指定<appender>的名称    class指定<appender>的全限定名  ConsoleAppender的作用是将日志输出到控制台-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--输出时间格式 %-5level:级别从左显示5个字符宽度-->
            <pattern>%date %-5level %logger{36}.%M\(%line\) -- %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 通过 "byModel" 将时间格式化成 "yyyyMMdd" 的形式插入到 logger 的上下文中这个值对后续的配置也适用-->
    <timestamp key="byModel" datePattern="yyyyMMdd" />

    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">

        <file>${byModel}.log</file>

        <!-- 配置日志所生成的目录以及生成文件名的规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${log.zip.path}/%d{yyyy-MM-dd}.%i.log.zip</FileNamePattern>
            <!-- 日志总保存量为1GB -->
            <totalSizeCap>1024MB</totalSizeCap>
            <!-- 如果按天来回滚,则最大保存时间为365天,365天之前的都将被清理掉 -->
            <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!--文件达到 最大128MB时会被压缩和切割 -->
                <maxFileSize>128MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

        <encoder>
            <pattern>%date %-5level %logger{36}.%M\(%line\) -- %msg%n</pattern>
        </encoder>

    </appender>

    <logger name="top.testeru" level="DEBUG" />
    <logger name="ch.qos" level="OFF" />
    <!--    <logger name="io.qameta.allure" level="OFF" />-->
    <logger name="org" level="WARN" />

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

总结

  • 特殊元素定位:toast 元素 使用xpath
  • 等待方式:显式等待
  • 获取页面源码:getPageSource

企业微信 - 打卡

  • 前提条件:

    • 1、提前注册企业微信管理员帐号
    • 2、手机端安装企业微信
    • 3、企业微信 app 处于登录状态

企业微信 - 打卡

  • 实现打卡功能
    • 打开【企业微信】应用
    • 进入【工作台】页面
    • 点击【打卡】
    • 选择【外出打卡】tab
    • 点击【第 N 次打卡】
  • 验证点:提示【·正常】

总结

  • DesireCapbility 动态设置
  • 封装滑动操作
  • 显式等待