数据驱动:以数据来驱动整个测试用例的执行,测试数据决定测试结果
数据驱动的实现要依赖参数化的技术
传入数据的方式:
- 直接定义在测试脚本中
- 从文件读取数据,如JSON,excel,xml,txt等格式文件
- 从数据库中读取数据
- 直接调用接口获取数据源
- 本地封装一些生成数据的方法
示例一:对网页计算器,进行加法的测试操作,通过读取数据文件中的数据来执行用例
PO模式实现
utils
'''
通用工具类
'''
from selenium import webdriver
class UtilsDriver:
_driver = None
@classmethod
def get_driver(cls):
if cls._driver is None:
cls._driver = webdriver.Chrome()
cls._driver.maximize_window()
cls._driver.implicitly_wait(10)
cls._driver.get('http://cal.apple886.com/')
return cls._driver
@classmethod
def quit_driver(cls):
if cls._driver:
cls.get_driver().quit()
cls._driver = None
base_page
from selenium.webdriver.support.wait import WebDriverWait
from test_data.utils import UtilsDriver
# 基类的对象层
class BasePage:
def __init__(self):
self.driver = UtilsDriver.get_driver()
def get_element(self, location):
wait = WebDriverWait(self.driver, 10, 1)
element = wait.until(lambda x: x.find_element(*location))
return element
# 基类的操作层
class BaseHadle:
# 暂时没用到
def input_text(self, element, text):
pass
main_page
from selenium.webdriver.common.by import By
from test_data.base.base_page import BasePage
# 首页的对象层,定位元素
class MainPage(BasePage):
def __init__(self):
super().__init__()
# 获取数字元素
self.digit_btn = (By.ID, 'simple{}')
# 获取加号元素
self.add_btn = (By.ID, 'simpleAdd')
# 获取等号元素
self.eq_btn = (By.ID, 'simpleEqual')
# 获取结果
self.res = (By.ID, 'resultIpt')
def find_digit_btn(self, digit):
'''
获取数字元素
:return:
'''
location = (self.digit_btn[0], self.digit_btn[1].format(digit))
return self.get_element(location)
def find_add_btn(self):
'''
获取加号元素
:return:
'''
return self.get_element(self.add_btn)
def find_eq_btn(self):
'''
获取等号元素
:return:
'''
return self.get_element(self.eq_btn)
def find_res(self):
'''
获取结果
:return:
'''
return self.get_element(self.res)
# 首页的操作层,对元素的操作
class MainHandle:
def __init__(self):
self.main_page = MainPage()
def click_digit_btn(self, digit):
'''
点击数字
:return:
'''
self.main_page.find_digit_btn(digit).click()
def click_add_btn(self):
'''
点击加号
:return:
'''
self.main_page.find_add_btn().click()
def click_eq_btn(self):
'''
点击等号
:return:
'''
self.main_page.find_eq_btn().click()
def get_res(self):
'''
获取结果
:return:
'''
return self.main_page.find_res().get_attribute("value")
def input_numbers(self, numbers):
'''
输入计算数字
:param numbers:
:return:
'''
for num in numbers:
self.click_digit_btn(num)
# 首页的业务层,一个业务功能
class MainProxy:
def __init__(self):
self.main_handle = MainHandle()
def add(self, num1, num2):
'''
完成一次加法运算
:return:
'''
self.main_handle.input_numbers(str(num1))
self.main_handle.click_add_btn()
self.main_handle.input_numbers(str(num2))
self.main_handle.click_eq_btn()
def get_res(self):
'''
获取计算结果
:return:
'''
return self.main_handle.get_res()
test_add
import json
import pytest
from test_data.page.main_page import MainProxy
from test_data.utils import UtilsDriver
def build_data():
test_data = []
with open("../data_driver/calculator.json", encoding='UTF-8') as f:
test_data = json.load(f)
print("test_data=", test_data)
return test_data
class TestAdd:
def setup_class(self):
# 实例化业务对象
self.main_proxy = MainProxy()
def teardown_class(self):
UtilsDriver.quit_driver()
@pytest.mark.parametrize('num1,num2,expect', build_data())
def test_add(self, num1, num2, expect):
'''
加法测试
:return:
'''
print('num1={} num2={} expect={}'.format(num1, num2, expect))
self.main_proxy.add(num1, num2)
res = self.main_proxy.get_res()
print("result=", res)
assert res == str(expect)
calculator.json
[
[1,1,2],
[2,2,4],
[1,0,1],
[0,0,1],
[12,23,35]
]
结构
结果
示例二: 实现登录模块的单元测试,对上次的改造
之前的test_login
# 定义测试类
from test_po.page.login_page import LoginProxy
from test_po.page.main_page import MainProxy
from test_po.utils import UtilsDriver, get_msg
class TestLogin:
def setup_class(self):
# 实例化首页和登录的业务对象
self.main_proxy = MainProxy()
self.login_proxy = LoginProxy()
# 通过首页页面的业务层操作实现登录的跳转
self.main_proxy.go_login_page()
# 创建方法级别的fixture初始化的操作方法
def setup(self):
UtilsDriver.get_driver().refresh()
# 创建类级别fixture销毁的操作方法
def teardown_class(self):
UtilsDriver.quit_driver()
# 定义测试方法 账号不存在
def test_login_01(self):
self.login_proxy.login("13333337777", "123456")
msg = get_msg()
assert "账号或密码错误,请重试。" in msg
# 定义测试方法 密码错误
def test_login_02(self):
self.login_proxy.login("cherishgf", "123456")
# 获取提示信息
msg = get_msg()
assert "账号或密码错误。" in msg
# 定义测试方法 用户名为空
def test_login_03(self):
self.login_proxy.login(" ", "123456")
# 获取提示信息
msg = get_msg()
assert "继续操作前请注册或者登录。" in msg
修改后的test_login
# 定义测试类
import json
import pytest
from test_po.page.login_page import LoginProxy
from test_po.page.main_page import MainProxy
from test_po.utils import UtilsDriver, get_msg
def build_data():
test_data = []
with open('../data_driver/test_data.json', encoding='utf-8') as f:
json_data = json.load(f)
for case_data in json_data:
test_data.append((case_data.get("username"),
case_data.get("password"),
case_data.get("is_success"),
case_data.get("expect")))
print(test_data)
return test_data
class TestLogin:
def setup_class(self):
# 实例化首页和登录的业务对象
self.main_proxy = MainProxy()
self.login_proxy = LoginProxy()
# 通过首页页面的业务层操作实现登录的跳转
self.main_proxy.go_login_page()
# 创建方法级别的fixture初始化的操作方法
def setup(self):
UtilsDriver.get_driver().refresh()
# 创建类级别fixture销毁的操作方法
def teardown_class(self):
UtilsDriver.quit_driver()
# 定义测试方法
@pytest.mark.parametrize('username, password,is_success, expect', build_data())
def test_login(self, username, password, is_success, expect):
print('username={} password={} is_success={} expect={}'.
format(username, password, is_success, expect))
self.login_proxy.login(username, password)
msg = get_msg()
assert expect in msg
结果