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