数据驱动
- 安装:
pip install ddt
- Demo,数据打包,给测试类、测试case装饰上,这就是PO模型+UT+DDT
import ddt import unittest @ddt.ddt class DataTest(unittest.TestCase): @ddt.data( ['1', '2'], [3, 4], [5, 6] ) @ddt.unpack def test_data_add(self, a, b): print(a+b) if __name__ == '__main__': unittest.main()
- PO模型+UT+DDT测试流程
- 将主要的逻辑放在business层,case层逻辑还是最简化,这里主要体现出数据驱动case
- 以测试邮箱输入失败的case为例;输入错误格式的邮箱地址 -> 页面显示错误信息 -> assertFalse执行成功,完全体现数据驱动
import os.path import time import ddt import unittest from selenium import webdriver class FirstDdtCase(unittest.TestCase): def setUp(self) -> None: self.driver = webdriver.Chrome() self.driver.get("'http://www.5itest.cn/register'") self.login = RegisterBUsiness(self.driver) def tearDown(self) -> None: time.sleep(2) for method, error in self._outcomes.errors: if error: case_name = self._testMethodName + '.png' file_path = os.path.join(os.getcwd(), '/report/', case_name) self.driver.save_screenshot(file_path) self.driver.close() @ddt.data( # 邮箱,用户名,密码,验证码,错误信息标识,错误信息内容 # ['email', 'username', 'password', 'code', 'assertCode', 'assertText'], ['666', 'roy', 123, 'xxxx', 'user_email_error', '请输入有效的电子邮件地址'] ) @ddt.unpack def test_register_case(self, email, username, password, code, assertCode, assertText): # 这里封装的接口只需要传入数据,剩下的都在business做,完全体现data驱动 # register_function(business层)包含注册的全流程,并不针对email_error # 注册时不同的场景也都是通过data体现的,email传不合法值,assertCode/assertText给上对应的值,就OK,完全体现ddt email_error = self.login.register_function(email, username, password, code, assertCode, assertText) return self.assertFalse(email_error, '测试失败')
- 幕后的page层和config文件要对应上
- 以文件形式实现数据驱动
- 在config下创建data.xls文件,存放数据,每个单元格数据都要搞成文本格式
- 安装操作Excel的包:
pip install xlrd
import xlrd class ExcelHandle: def __init__(self, ex_path=None, index=None): if ex_path is None: ex_path = '../conf/data.xls' if index is None: # sheet index = 0 self.data = xlrd.open_workbook(ex_path) self.table = self.data.sheets()[index] self.rows = self.table.nrows # 这张sheet的行数 def get_data(self): result = [] for row in range(self.rows): r = self.table.row_values(row) # 列表 result.append(r) return result
from util.excel_handle import ExcelHandle DATA = ExcelHandle.get_data() class FirstDdtCase(unittest.TestCase): ... @ddt.data(*DATA) def test_register_case(self, data): # 再次解包 email, username, password, code, assertCode, assertText = data email_error = self.login.register_function(email, username, password, code, assertCode, assertText) return self.assertFalse(email_error, '测试失败')
关键字模型
- DDT通过数据文件减少了我们的case量,那如何让没代码基础的人也能写测试代码?
- 以测试注册为例,我们发现主要包含两个操作:输入,点击
- 还是结合DDT的思想,所以我们搞了个Excel表格,什么操作,调用哪个方法都写清楚,直接用数据驱动
- 这里的“操作”就是所谓的关键字,case步骤都是通过这一列判别的
- 这个关键字驱动也是按照流程一步步执行
- 这里废话太多不想说了,直接看RobotFramework,结合Python源码看一看
- 层级关系:Keywords -> TestCases -> Suites
- 官方说这是关键字驱动的框架,关键字描述的功能用python封装,可能是因为Case都是调Keyword组装的;但更像BDD(给不会写代码的人用的)
- 这个不是DDT的思想,但也比较流行,记录清楚流程后会在这里更新笔记
行为驱动
- BDD(behavior driven development)像说话一样写Case
- 框架:Behave
- 安装
pip install behave
pip install pyhamcrest
- 来个简单的例子看看怎么用
- 定义目录结构
- register.feature文件
Feature: Register User As a developer This is my first bdd project Scenario: open register website When I open the register website Then I expect that the title is "注册"
- environment.py,设置环境变量,注意:文件名都是behave规定的,不要乱写
from selenium import webdriver def before_all(context): ''' :param context: 全局超级变量 :return: ''' context.driver = webdriver.Chrome() def after_all(context): context.driver.close()
- register_user.py 实现具体逻辑
from behave import * use_step_matcher('re') @when('I open the register website') def step_register(context): context.driver.get('http://www.5itest.cn/register') # 匹配除"的所有字符 [^"]*,这个要和feature文件中keyword的定义一致;可以直接用 .* @then('I expect that the title is "([^"]*)"') def step_register_exp(context, title_name): title = context.driver.title assert title_name in title
- terminal执行
- 可以发现,和robotframework的流程基本一致,
Feature
相当于Suite,Scenario
相当于TestCase,When xxx
相当于Keyword,底层逻辑在register_user
用Python实现
- 定义目录结构
- 接下来在新的case(scenario)填写具体的注册信息,注意:这里的
and
和Python关键字冲突,需要大写
- 结合PO模型,封装我们的register_user
- 新建lib文件夹
- 基础类,主要定义基础操作,通用
class BasePage: def __init__(self, driver): self.driver = driver # 打开网页 def get_url(self, url): self.driver.get(url) def get_title(self): return self.driver.title # 定位元素 def find_element(self, *loc): self.driver.find_element(*loc)
- register_page.py,针对某个feature
from features.lib.pages.base_page import BasePage from selenium.webdriver.common.by import By class RegisterPage(BasePage): def __init__(self, context): # 使用父类的初始化方法 super(RegisterPage, self).__init__(context.driver) def send_useremail(self, useremail): self.find_element(By.ID, 'register_email').send_keys(useremail) def send_username(self, username): self.find_element(By.ID, 'register_nickname').send_keys(username)
- 使用我们封装的方法
from behave import * from features.lib.pages.register_page import RegisterPage use_step_matcher('re') @when('I open the register website') def step_register(context): # context.driver.get('http://www.5itest.cn/register') RegisterPage.get_url('http://www.5itest.cn/register') @then('I expect that the title is "([^"]*)"') def step_register_exp(context, title_name): title = RegisterPage.get_title() # title = context.driver.title assert title_name in title @when('I set with useremail "([^"]*)"') def step_register(context, useremail): RegisterPage(context).send_useremail(useremail) @when('I set with username "([^"]*)"') def step_register(context, username): RegisterPage(context).send_username(username)
- 新建lib文件夹