pytest是python的一种单元测试框架
pytest基础
| def test_1(): |
| name = "七安" |
| assert name == "七安" |
输入pytest执行
文件命名规范
| (1).py测试文件必须以“test_”开头(或“_test”结尾) |
| (2)测试方法必须以“test_”开头 |
| (3)测试类必须以Test开头,并且不能有init方法 |
| class TestCase: |
| def test_two(self): |
| assert 1 == 1 |
断言类型
| 等于:== |
| 不等于:!= |
| 大于:> |
| 小于:< |
| 属于:in |
| 不属于:not in |
| 大于等于:>= |
| 小于等于:<= |
| 是:is |
| 不是:is not |
| def test_three(): |
| assert 1 == 1 |
| assert 1 != 2 |
| assert 3 > 1 |
| assert 1 < 3 |
| assert 3 >= 3 |
| assert 3 <= 3 |
| assert "a" in "abc" |
| assert "d" not in "abc" |
| assert True is True |
| assert False is not True |
| def test_1(): |
| name = "七安" |
| assert name == "七安" |
| def test_2(): |
| num = 10 |
| assert num == 10 |
assume插件
失败继续执行
| pytest.assume("e" in "abc") |
| pytest.assume("a" not in "abc") |
pytest+selenium
| def test_baidu(): |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| driver.get("https://www.baidu.com/") |
| title = driver.title |
| url = driver.current_url |
| text = driver.find_element(By.CSS_SELECTOR, 'a[href="http://news.baidu.com"]').text |
| button_text = driver.find_element(By.ID, 'su').accessible_name |
| assert title == "百度一下,你就知道" |
| assert url == "https://www.baidu.com/" |
| assert text == "新闻" |
| assert button_text == "百度一下" |
执行测试用例方法1
指定目录
pytest testcaes
指定文件
pytest testcaes/test_one.py
执行测试用例方法2
使用pytest.ini置文件,设置运行目录
使用markers标记
| [pytest] |
| ;路径 |
| testpaths = ./目录 |
| ;标签匹配 |
| markers= |
| pro:pro |
| test:test |
| p1:p1 |
| ;关键字匹配 |
| addpts: -k three |
关键字匹配
| @pytest.mark.pro |
| pytest -m 执行特定的测试用例 |
| pytest -k 执行用例包含"关键字"的用例 |
| pytest -q 说明:简化控制台的输出 |
| pytest -v 可以输出用例更加详细的执行信息 |
| pytest -s 输出我们用例中的调试信息 |
自定义运行顺序
pip install pytest-ordering
- 默认执行顺序类或方法
- 使用pytest-ordering自定义顺序
@pytest.mark.run(order=1)
setup_module和teardown_module
- 模块级 setup_module/teardwn_module 开始于模块,生效一次
- 函数级 setup_functin/teardown_function 对每条函数用例生效(不在类中)
- 类级 setup_class/teardwn_class 只在类中前后运行一次(在类中)
- 方法级 setup_method/teardown_method 开始于方法末(在类中)
| def setup_module(): |
| global driver |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器,并最大化") |
| |
| def teardown_module(): |
| driver.close() |
| driver.quit() |
| print("关闭浏览器") |
| def setup_function(): |
| global driver |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器,并最大化") |
| |
| def teardown_function(): |
| driver.close() |
| driver.quit() |
| print("关闭浏览器") |
| |
| class TestBaidu: |
| def setup_class(self): |
| global driver |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器,并最大化") |
| |
| def teardown_class(self): |
| driver.close() |
| driver.quit() |
| print("关闭浏览器") |
| class TestBaidu(): |
| def setup_method(self): |
| global driver |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器,并最大化") |
| def teardown_method(self): |
| driver.close() |
| driver.quit() |
| print("关闭浏览器") |
三种跳过测试用例方法
| @pytest.mark.skip(reason="我不想执行") |
| def test_skip(): |
| assert 1 == 1 |
| print("我不想执行") |
| # 执行 不常用 |
| @pytest.mark.skipif(1 == 2, reason="判断1等于1") |
| def test_skip2(): |
| assert 1 == 1 |
| print("我执行了") |
| a = 1 |
| def test_skip3(): |
| if a > 1: |
| pytest.skip("测试用例中跳过") |
| assert 1 == 1 |
| |
| @pytest.mark.skipif(True,reason='true执行') |
| pytest.skip('描述') |
pytest进阶
fixture夹具,@pytest.fixtrue
| (scope="function")每一个函数或方法都会调用 |
| (scope="function",aotuuse=True) |
| |
| @pytest.fixture(scope="function") |
| def fixture1(): |
| print("我是前置步骤1") |
| return 1 |
| @pytest.fixture(scope="function") |
| def fixture2(): |
| print("我是前置步骤2") |
| return 1 |
| def test_fixture1(fixture1, fixture2): |
| assert fixture1 == 1 |
| assert fixture2 == 1 |
| def test_fixture2(): |
| assert 1 == 1 |
| if __name__ == '__main__': |
| pytest.main() |
| |
| (scope="class")每一个类调用一次 |
| |
| @pytest.fixture(scope="class") |
| def driver(): |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器") |
| return driver |
| |
| (scope="module")每一个.py文件调用一次 |
| (scope="session")是多个文件调用一次,.py文件就是module |
| fixture的作用范围:session>module>class>function |
conftest.py
- conftest.py为固定写法,不可修改名字
- 使用conftest.py文件方法无需导入
- 函数作用于当前文件夹及下属文件夹
- 最外层创建conftest所有都可调用
| @pytest.fixture(scope="session") |
| def driver(): |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器") |
| return driver |
fixture夹具,使用yield做后置
| @pytest.fixture(scope="session") |
| def driver(): |
| driver = webdriver.Chrome() |
| driver.maximize_window() |
| print("打开浏览器") |
| yield driver |
| print("关闭浏览器") |
| driver.quit() |
| @pytest.fixture(autouse=True) |
| def fixture(): |
| print("我是前置步骤") |
| yield "七安" |
| print("我是后置步骤") |
| |
| def test_yield(fixture): |
| print(fixture) |
| assert 1 == 1 |
参数化(parametrize)
单参数
| |
| @pytest.mark.parametrize("key", ["value"]) |
| def test_parametrize01(key): |
| print(key) |
| |
| @pytest.mark.parametrize("test", ["接口自动化", 'UI自动化', '性能测试']) |
| def test_parametrize02(test): |
| print("我现在会" + test) |
| assert test == "性能测试" |
| |
| @pytest.mark.parametrize("key", ["接口自动化", 'UI自动化', '性能测试']) |
| def test_baidu(driver, key): |
| driver.get("https://www.baidu.com") |
| driver.find_element(By.ID, 'kw').send_keys(key) |
| driver.find_element(By.ID, 'su').click() |
| sleep(2) |
多参数
| |
| @pytest.mark.parametrize("hero,word", [("七安", "www.67an.cn")]) |
| def test_parametrize021(hero, word): |
| print("{}的台词是{}".format(hero, word)) |
| |
| @pytest.mark.parametrize("hero,word", [("七安", "www.67an.cn"), ("67an", "www.67an.com")]) |
| def test_parametrize02(hero, word): |
| print("{}的台词是{}".format(hero, word)) |
| |
| heros = [("七安", "www.67an.cn"), ("67an", "www.67an.com")] |
| @pytest.mark.parametrize("hero,word", heros) |
| def test_parametrize03(hero, word): |
| print("{}的台词是{}".format(hero, word)) |
| |
| @pytest.mark.parametrize("key,value", [["七安", "七"], ["67an", "7"]]) |
| def test_parametrize(key, value): |
| |
| assert value in key |
| print("{}的台词是{}".format(key, value)) |
| |
| @pytest.mark.parametrize("username,password", [("admin", "123456"), ("test", "123456")]) |
| def test_login(driver, username, password): |
| driver.get("https://www.**.***/***") |
| driver.find_element(By.XPATH, '//input[@id="user_login"]').send_keys(username) |
| driver.find_element(By.XPATH, '//input[@id="user_pass"]').send_keys(password) |
| driver.find_element(By.XPATH, '//input[@id="wp-submit"]').click() |
| sleep(2) |
参数化可以组装测试数据,在测试前定义好测试数据,并在测试用例中使用
参数值为字典形式
| @pytest.mark.parametrize("data", [{"name": "七安", "word": "技术宅拯救世界"}]) |
| def test_parametrize03(data): |
| print(data["name"]) |
| print(data["word"]) |
YAML
YAML是一个对所有编程语言都很友好的数据序列化标准它是一种直观的能够被电脑识别的数据序列化格式,是一种可读性高且容易被人类阅读的脚本语言(不仅仅是Python)交互,用于表达资料序列的编程语言。YAML语言的本质是一种通用的数据串行化格式。
适用场景
- 在脚本语言中使用,实现简单,解析成本低;
- 序列化;
- 编程时写配置文件,比xml快,比ini文档功能更强。
- YAML是专门用于写配置文件的语言,非常简洁和强大,远比JSON格式方便。
YAML支持的三种数据结构
- 对象:即键值对的集合,又称为映射(mapping)/哈希(hashes)/字典(dictionary);
- 数组:一组按次序排列的值,又称为序列(sequence)/列表(list)
- 纯量:单个的、不可再分的值
对象
| key: |
| child-key:value |
| child-key2:value2 |
等于
{"key":{"child-key":"value","child-key2":"value2"}}
数组
等于
{"key":[A,B,C]}
组合
| key: |
| -child-key:value |
| child-key2:value2 |
等于
{"key":[{"child-key":"value","child-key2":"value2"}]}
数组嵌套
等于
{"key":[[A,B,C]]}
| data.yaml |
| userinfo: |
| username: admin |
| password: 123456 |
| skill: |
| - 接口自动化 |
| - UI自动化 |
| - 性能测试 |
| userinfos: |
| - username: admin |
| password: 123456 |
| - username: test01 |
| password: 123456 |
| userinfo_list: |
| - [ admin,123456 ] |
| - [ test01,123456 ] |
| skills: |
| - - 接口自动化 |
| - UI自动化 |
| - 性能测试 |
| skills2: |
| - [ 接口自动化,UI自动化,性能测试 ] |
| |
| read.py |
| # 导入path |
| def get_yaml_path(): |
| path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "data", "data.yaml") |
| return path |
| path = get_yaml_path() |
| def read_yaml(): |
| data = yaml.safe_load(f) |
| return data |
| if __name__ == '__main__': |
| print(read_yaml()['userinfo']['username']) |
| print(read_yaml()) |
| print(read_yaml()['skill']) |
parametrize+yaml
| @pytest.mark.parametrize("key", read_yaml()[ |
| def test_baidu(driver, key): |
| driver.get("https://www.baidu.com") |
| driver.find_element(By.ID, |
| driver.find_element(By.ID, |
| sleep(2) |
| @pytest.mark.parametrize("data", read_yaml()['userinfos']) |
| def test_login(driver, data): |
| driver.get("https://***.***.***/***") |
| driver.find_element(By.XPATH, '//input[@id="user_login"]').send_keys(data['username']) |
| driver.find_element(By.XPATH, '//input[@id="user_pass"]').send_keys(data['password']) |
| driver.find_element(By.XPATH, '//input[@id="wp-submit"]').click() |
| sleep(2) |
| @pytest.mark.parametrize("username,password", read_yaml()[ |
| def test_login(driver, username,password): |
| driver.get("https://***.***.***/***") |
| driver.find_element(By.XPATH, |
| driver.find_element(By.XPATH, |
| driver.find_element(By.XPATH, |
| sleep(2) |