Appium自动化测试 —— 断言

news2024/11/18 18:19:49

在这里插入图片描述

 
 

在这里插入图片描述
😏作者简介:博主是一位测试管理者,同时也是一名对外企业兼职讲师。
📡主页地址:【Austin_zhai】
🙆目的与景愿:旨在于能帮助更多的测试行业人员提升软硬技能,分享行业相关最新信息。
💎声明:博主日常工作较为繁忙,文章会不定期更新,各类行业或职场问题欢迎大家私信,有空必回。

在这里插入图片描述

 
 

阅读目录

  • 1.目的
  • 2.说明
  • 3.接上回
  • 4. 概念
  • 5. 基础断言
    • 5.1 assertEqual
    • 5.2 assertTrue
    • 5.3 assertIn
    • 5.4 assertIs
  • 6. 组合断言
  • 7. 注意点

1.目的

在这里插入图片描述

  当今社会,人们的生活几乎已经无法离开各种各样的APP了,它提供给我们的便利与服务意义远远超出了其本身的软件价值。作为测试来说移动应用也早已是各大互联网公司的拳头产品,其本身的开发周期短,附属产品价值高等特性决定了今后的主导地位。

  那么在日常的测试活动中,移动应用的质量保障就成为了各个测试团队的主要课题,面对高速迭代的功能、日益缩短的项目周期、逐渐庞大的人力与资源投入,以上的这些因素都会让测试团队不得不在项目中加入自动化测试策略。

  Appium作为一个自动化移动应用测试框架来说,就可以很好的满足大部分移动应用测试的需求。作为当今仍然主流的自动化测试框架,各位测试同学要熟练的使用也就成为大家日常的基本内容之一。

 
 

2.说明

在这里插入图片描述

  1.此笔记的中所使用的操作系统为Win 11,笔记中所涉及的软件版本有可能会因为时间的推移而导致不匹配或其他额外的操作,请大家有针对性的选择阅读与参考。
  2.这里因操作系统关系,只针对安卓与鸿蒙OS来进行教程讲解,后续会推出iOS的相关Appium安装与配置、日常使用教程。
  3.本文是使用Python语言配合Appium进行讲解,其他语言相关教程不在此做赘述。

 
 

3.接上回

在这里插入图片描述

  通过《自动化测试之路》系列与《Appium PO模式UI自动化测试框架——设计与实践》的大致介绍,我们已经熟练地掌握了Appium自动化测试中的基础使用方法与何使用appium中的PO模式UI自动化测试框架的基础架构,接下来博主会将其中的大部分流程进行拆解并进行详细的讲解,方便大家加深印象。今天我们就从最基础的断言方法来进行介绍。

 
 

4. 概念

在这里插入图片描述

  说到断言,相信大家也一定是如数家珍了吧,没错,断言就是用来检查程序执行结果是否符合预期的一种机制或语句。在日常的测试活动中,我们的软测人员通过使用断言,在测试代码中明确指定某个条件,并在程序运行时对该条件进行检查。如果条件满足,那么即预期结果与实际结果一致,断言会通过,测试继续执行。但如果条件不满足,即预期结果与实际结果不一致,断言会失败,测试会停止,并抛出一个指定的错误消息。

  断言就和我们手工测试用例中的预期结果一样,缺少了它,你的测试用例就会变得毫无意义。当然使用了断言不单单会使我们的测试用例变得完整,而且它可以帮助我们快速发现代码中的错误和问题,尤其在自动化测试中。它们可以验证函数的返回值、对象的属性、数据结构的状态以及其他各种条件。另外在断言失败时可以抛出我们指定的错误信息,也正是这样的机制让我们的测试人员可以在大量的测试用例与代码中快速的定位失败用例出现问题的大致原因,加快问题修复的周期。

 
 

5. 基础断言

在这里插入图片描述

  下面我们就针对之前PO模式下的UI自动化测试框架中的一些断言方式来做详细的讲解与分析。

 

5.1 assertEqual

  出镜率极高的一个断言方式,和assertTrue差不多的节奏。它表示“断言相等”。在框架中,它是一种用于验证两个值是否相等的语句,使用 assertEqual 断言时,它会比较两个值是否相等。如果它们相等,那么断言通过,测试继续执行;如果它们不相等,那么断言失败,测试会停止,并抛出一个错误消息。

self.assertEqual(actual_value, expected_value, "err_msg")

实例:判断两个元素是否相等,判断的元素从业务页面进行抓取并与既定预期值进行判断。

import unittest
from appium import webdriver

class AppElementCatch(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.xxxxxx.app',
            'appActivity': 'MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_element_id(self):
        element_b = self.driver.find_element_by_id('element_b_id')

        element_a_id = self.driver.find_element_by_id('edt_customer_name').get_attribute('resourceId')

        self.assertEqual(element_b.get_attribute('resourceId'), element_a_id, "元素ID不匹配。")

 
 

5.2 assertTrue

   assertTrue 表示“断言为真”或“断言为真值”。我们传入一个条件表达式,并期望它为真。如果条件为真,那么断言通过,测试继续执行,反之则用例不通过。通常情况下,assertTrue用于验证某个条件是否满足,例如检查函数返回值是否为真、验证某个标志是否被设置、判断某个条件是否为真、两个元素的属性值是否相等的结果为真等。

self.assertTrue(condition, "条件为假。")

实例:判断页面上的两个按钮元素的class属性是否一致,同理,我们可以使用assertTrue来判断我们要验证的这件事的结果是否为真,相同为真,不相同为假。

import unittest
from appium import webdriver

class MyTestCase(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.xxxx.app',
            'appActivity': 'MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_button_class(self):
        button1 = self.driver.find_element_by_id('button1_id')
        button2 = self.driver.find_element_by_id('button2_id')

        button1_class = button1.get_attribute('class')
        button2_class = button2.get_attribute('class')

        self.assertTrue(button1_class == button2_class, "按钮的class属性值不相同。")

 
 

5.3 assertIn

   assertIn表示“断言存在”。它一般是用来验证某个值是否存在与指定的容器之中。就好比你有一个容器,比如一个列表或者字典,你想要验证某个值是否存在于其中。你可以使用 assertIn 来断言这个值是否存在于容器中。所以使用 assertIn 断言可以帮助你快速验证某个值是否在容器中,并确保程序在不同情况下的数据是否符合预期。

self.assertIn(item, container, "对象不存在于此容器中。")

实例:我们使用assertIn来判断某个页面上一个下拉框组件是否存在于指定的容器内,

import unittest
from appium import webdriver

class EcommerceTestCase(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.xxxx.app',
            'appActivity': 'MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_dropdown_in_container(self):
        dropdown_element = self.driver.find_element_by_id('dropdown_id')
        container_element = self.driver.find_element_by_id('container_id')

        dropdown_options = dropdown_element.text.split('\n')
        container_options = container_element.text.split('\n')
        
        for option in dropdown_options:
            self.assertIn(option, container, f"下拉框选项'{option}'不存在于该容器内。")

 
 

5.4 assertIs

   assertIs表示“断言为同一对象”,与编程语言中的is是相同的作用。同一对象的意思是断言的前后两个元素是否引用了同一个对象。我们一般用来验证函数返回的对象是否是同一个实例、判断两个变量是否引用同一个对象、检查对象是否符合预期等。所以通俗的来说,当我们需要在测试中验证对象的身份,确保它们是同一个实例的时候,就可以使用assertIs来进行断言。

self.assertIs(obj1, obj2, "两个对象不相同。")

实例:我们用assertIs来验证某些验证对象是否与业务页面中列表内的指定对象是否为同一个。

import unittest
from appium import webdriver

class FinancialTestCase(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.example.app',
            'appActivity': 'MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_same_product_in_list(self):
        # 假设在金融产品列表页面,通过 find_element 定位产品名称元素
        product1_element = self.driver.find_element_by_id('product1_name_id')
        product2_element = self.driver.find_element_by_id('product2_name_id')

        # 获取产品名称文本
        product1_name = product1_element.text
        product2_name = product2_element.text

        # 使用 assertIs 断言验证两个产品是否为同一个
        self.assertIs(product1_name, product2_name, "指定的对象不相同。")

 
 

一些与上面断言相反的断言方法,比如assertNotEqual、assertNotEqual、assertNotIn、assertIsNot在这里就不展开介绍了,作用的场景与以上这些相反,大家可以根据自己的实际测试业务场景灵活设计使用即可。

 
 

6. 组合断言

在这里插入图片描述

  组合断言,顾名思义,就是我们在一个测试用例中使用多个断言方法来组合使用,通过组合断言我们可以对更复杂的测试业务场景来进行实际的结果验证。

实例:比如我们需要在某款金融系统中去计算贷款利率(这里主要用于演示组合断言,简化了真实业务的算法与行业规则),我们需要验证贷款利率的计算结果是否正确。第一个断言要验证计算得到的利率等于预期利率,第二个则要验证验证计算得到的利率等于预期利率。

import unittest

class LoanTestCase(unittest.TestCase):
    def test_interest_calculation(self):
        principal_amount = 100000
        interest_rate = 0.05
        expected_interest_amount = 5000

        actual_interest_rate, actual_interest_amount = calculate_loan_interest(principal_amount, interest_rate)

        self.assertEqual(actual_interest_rate, interest_rate, "贷款利率计算错误。")
        self.assertEqual(actual_interest_amount, expected_interest_amount, "贷款利息金额计算错误。")

 
 

实例2:比如你需要验证某个投资产品的收益率是否在预期范围内,我们的第一个断言需要验证收益率大于等于最低预期收益率,第二个断言需要验证验证收益率小于等于最高预期收益率。

import unittest

class InvestmentTestCase(unittest.TestCase):
    def test_profit_rate(self):
        expected_min_profit_rate = 0.05
        expected_max_profit_rate = 0.1
        
        actual_profit_rate = get_investment_profit_rate()

        self.assertGreaterEqual(actual_profit_rate, expected_min_profit_rate, "利润率低于预期的最低限度。")
        self.assertLessEqual(actual_profit_rate, expected_max_profit_rate, "利润率超过了预期最大值。")

 
 

到这里可能会有些同学看不太明白,这里我们在用基本的页面元素来演示一下组合断言的测试场景。

实例3:比如我需要验证某个产品详情页是否有多个预期的元素存在,那么这里我们就可以使用组合断言的方法来进行确认。

import unittest
from appium import webdriver

class FinancialProductTestCase(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.xxxx.app',
            'appActivity': 'com.example.app.MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_product_details(self):
        product_name_element = self.driver.find_element_by_id('txt_product_name')
        profit_rate_element = self.driver.find_element_by_id('txt_profit_rate')
        term_element = self.driver.find_element_by_id('txt_term')

        self.assertIsNotNone(product_name_element, "产品名元素未找到。")
        self.assertIsNotNone(profit_rate_element, "利润率元素未找到。")
        self.assertIsNotNone(term_element, "期限元素未找到。")

 
 

实例4:比如你需要验证期货交易系统给登录页面中用户登录的多个流程是否成功,同样也可以使用组合断言的方式来进行验证。

import unittest
from appium import webdriver

class LoginTestCase(unittest.TestCase):
    def setUp(self):
        desired_caps = {
            'platformName': 'Android',
            'deviceName': 'device',
            'appPackage': 'com.xxxx.app',
            'appActivity': 'com.example.app.MainActivity'
        }
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    def tearDown(self):
        self.driver.quit()

    def test_login_flow(self):
        username_element = self.driver.find_element_by_id('edt_username')
        password_element = self.driver.find_element_by_id('edt_password')
        login_button_element = self.driver.find_element_by_id('btn_login')

        username_element.send_keys('testuser')
        password_element.send_keys('password123')

        login_button_element.click()

        home_element = self.driver.find_element_by_id('txt_home')

        self.assertIsNotNone(username_element, "输入用户名组件未找到。")
        self.assertIsNotNone(password_element, "输入密码组件未找到。")
        self.assertIsNotNone(login_button_element, "登录按钮未找到。")
        self.assertIsNotNone(home_element, "返回主页组件为找到。")

 
 

7. 注意点

在这里插入图片描述

  1. 我们在设计断言的时候,必须确保被测对象元素已经加载完成,所以像各类等待的方法一定要添加,以防测试用例即使有了断言也一样无法找到需要捕捉的元素对象,使得用例毫无意义;
  2. 断言的方法尽量使用精准的方法,避免使用一些模糊匹配的断言方法,比如判断一个区间内的数字或结果,这些都无法精准的定位到你所需要的准确结果,往往会因为一些不可预知的输出结果而导致测试用例异常通过;
  3. 在我们执行测试用例的时候,除了在断言中添加特定的异常信息外,在其之后进行更加完善的异常处理操作,无论是报错后记录日志还是进行自愈处理都是不错的选择,这些都可以让你的测试用例更加的健壮。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/706795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

六.函数的定义与调用

目录 一.内置函数: 二.标准库函数 三、自定义函数 1、函数定义 2、函数调用 3、函数参数 值传递: 引用传值: 4、函数返回多个值 5、defer语句 6、init函数: 一.内置函数: Go 语言拥有一些不需要进行导入操…

实现java代码加密,jar\war加密

Springboot 项目代码加密,对你的代码进行加密,市面工具无法实现反编译。加密 Class 文件中每个方法的 Java 字节码,运行时在 JVM实现动态解密。 支持的部署环境Windows/Linux/macOS支持的框架SpringMVC、SpringBoot、Maven场景java加固&…

JAVA-编程基础-08-try-catch性能探究

Lsion <dreamlison163.com>, v1.0.0, 2023.04.01 JAVA-编程基础-08-try-catch性能探究 文章目录 JAVA-编程基础-08-try-catch性能探究try-catch会影响性能吗&#xff1f; try-catch会影响性能吗&#xff1f; 在 for 循环里面搞了个 try-catch&#xff0c;不知道try-cat…

Java的SPI

JavaSPI&#xff0c;全称是ServiceProviderInterface。 它是一种基于接口的动态扩展机制&#xff0c;相当于Java里面提供了一套接口。然后第三方可以实现这个接口来完成功能的扩展和实现。 举个简单的例子。 在Java的SDK里面&#xff0c;提供了一个数据库驱动的接口java.sql.Dr…

ModaHub魔搭社区:向量数据库Milvus性能优化问题(一)

目录 性能优化问题 为什么重启 Milvus 服务端之后&#xff0c;第一次搜索时间非常长&#xff1f; 为什么搜索的速度非常慢&#xff1f; 如何进行性能调优&#xff1f; 应如何设置 IVF 索引的 nlist 和 nprobe 参数&#xff1f; 性能优化问题 为什么重启 Milvus 服务端之后…

如何创建你的第一个西门子200PLC程序

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲 创建西门子200PLC程序分五步&#xff1a;1.打开Micro/WIN软件&#xff1b;2.新建工程&#xff1b;3.打开程序编辑器&#xff1b;4.输入程序指令&#xff1b;5.保存程序。 我们以下图程序为例讲解西…

Unable to reload Maven project

原因就是IDEA的版本与Maven的版本冲突。 IDEA的日志报错如下&#xff1a; 我当时IDEA是2020版&#xff0c;Maven是3.8.5. 后来把Maven换成3.6.3版本就可以了。

途乐证券|沪深两市震荡整理 机器人概念成市场新热点

周四&#xff0c;A股三大指数维持窄幅震荡整理走势&#xff0c;沪深两市成交额仍维持在9000亿元下方&#xff0c;北向资金净卖出超70亿元。盘面上&#xff0c;减速器、机器人概念持续爆发&#xff0c;煤炭、电力板块领跌。 香港途乐证券有限公司&#xff08;191883.com&#xf…

【每日一题】Leetcode - 剑指 Offer 43. 1~n 整数中 1 出现的次数

题目 Leetcode - 剑指 Offer 43. 1&#xff5e;n 整数中 1 出现的次数 解题思路 分解数字中的每一位&#xff0c;判断记录 结果 class Solution {public int countDigitOne(int n) {int count 0;for (int i 1; i < n; i) {int localI i;while (localI / 10 ! 0) {in…

Leetcode-每日一题【143.重排链表】

题目 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; 请将其重新排列后变为&#xff1a; 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5]输出&#xff1a;[1,5,2,…

Ubuntu 23.10 现在由Linux内核6.3提供支持

对于那些希望在Ubuntu上尝试最新的Linux 6.3内核系列的人来说&#xff0c;今天有一个好消息&#xff0c;因为即将发布的Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;已经重新基于Linux内核6.3。 Ubuntu 23.10的开发工作于4月底开始&#xff0c;基于目前的临时版本Ubu…

光模块封装类型有哪些?光模块“皮肤”大揭秘

什么是光模块&#xff1f; 光模块&#xff08;Optical Transceiver&#xff09;全称为光收发一体模块&#xff0c;它是光通信中的核心器件&#xff0c;能够完成信号的光-电/电-光转换过程&#xff0c;它由光电子器件、功能电路和光接口等部件组成&#xff0c;其中的光电子器件…

【STL】iterator adapters_反向迭代器的实现

放在专栏【C知识总结】&#xff0c;会持续更新&#xff0c;期待支持 本章相关文章&#xff1a; 【STL】容器适配器 【STL】list的模拟实现 【STL】vector的模拟实现 1、反向迭代器介绍 1.1、前言 在前文中我们已经讲过STL中的适配器概念&#xff0c;即在底层将一个类的接口转…

centos7卸载自带jdk(openjdk)

前提&#xff1a;root模式 1、查看自带jdk 首先确定我们有自带的jdk&#xff0c;然后搜索jdk java -version rpm -qa | grep jdk 2、使用如下命令卸载openjdk rpm -e --nodeps [name] 复制带有openjdk的那两行&#xff0c;有的人可能有多行。一条条执行。 最后java -ver…

win10/11环境解决fastboot模式看不到设备

问题描述&#xff1a; C:\Users\good>adb devices * daemon not running. starting it now on port 5037 * * daemon started successfully * List of devices attached ? device C:\Users\good>fastboot devices fastboot devices 不是内部或外部命令&#xff0…

茶油生产加工MES质量溯源平台源码(spring boot+mybatis+easyui+mysql+h5)

一、生产加工MES&#xff08;Manufacturing Execution System&#xff0c;简称MES&#xff09;是一种面向车间的生产过程管理与实时信息系统。它主要负责监控生产过程&#xff0c;管理生产资源&#xff0c;优化生产流程&#xff0c;提高生产效率和质量。MES系统需要与ERP系统、…

【阻塞队列】阻塞队列DelayedWorkQueue源码详解

目录 一、前言 二、ScheduledThreadPoolExecutor线程池 三、DelayedWorkQueue延迟阻塞队列 四、工作原理 五、源码分析 5.1 定义 5.2 成员属性 5.3 构造函数 5.4 入队方法 5.4.1 offer添加元素 5.4.2 扩容grow() 5.4.3 向上堆化siftUp 5.5 出队方法 5.5.1 take()…

LangChain 基于 ChatGPT 构建本地知识库问答应用

一、使用本地知识库构建问答应用 上篇文章基于 LangChain 的Prompts 提示管理构建特定领域模型&#xff0c;如果看过应该可以感觉出来 ChatGPT 还是非常强大的&#xff0c;但是对于一些特有领域的内容让 GPT 回答的话还是有些吃力的&#xff0c;比如让 ChatGPT 介绍下什么是 L…

二重积分的解题技巧

计算方法 本节内容一般都应该先画图再思考后续内容较为直观 基本口诀是&#xff1a;后积先定限&#xff0c;限内画条线&#xff0c;先交写下限&#xff0c;后交写上限&#xff08;且下限必须小于上限&#xff09; 结合下图进行解释&#xff0c;后积先定限&#xff0c;对于X-型来…

Java基础-多线程JUC-多线程实现的三种形式

1. 第一种 继承Thread&#xff0c;重写run方法 public class demo1 {public static void main(String[] args) {/*** 多线程的第一种启动方式* 1. 定义一个类继承Thread* 2. 重写run方法* 3. 创建子类的对象&#xff0c;并启动线程*/MyThread myThread new MyThread();MyThrea…