亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> Python

PyTest 教程:什么是,如何安装,框架,断言

什么是 PyTest?

PyTest 是一个测试框架,允许用户使用 Python 编程语言编写测试代码。它可以帮助您为数据库、API 或 UI 编写简单且可扩展的测试用例。 PyTest 主要用于编写 API 测试。它有助于编写从简单的单元测试到复杂的功能测试的测试。

为什么要使用 PyTest?

pytest的一些优点是

在本 Python PyTest 教程中,您将学习:

如何安装 PyTest

以下是如何安装 PyTest 的过程:

步骤 1) 您可以通过

安装pytest
pip install pytest==2.9.1

安装完成后,您可以通过

进行确认
py.test -h

这将显示帮助

第一个基本 PyTest

现在,我们将通过一个基本的 PyTest 示例来学习如何使用 Pytest。

创建一个文件夹 study_pytest。我们将在这个文件夹中创建我们的测试文件。

请在命令行中导航到该文件夹​​。

在该文件夹中创建一个名为 test_sample1.py 的文件

添加下面的代码并保存

import pytest
def test_file1_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed"
def test_file1_method2():
	x=5
	y=6
	assert x+1 == y,"test failed" 

使用命令运行测试

py.test

你会得到输出

test_sample1.py F.
============================================== FAILURES ========================================
____________________________________________ test_sample1 ______________________________________
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed"
E       AssertionError: test failed
E       assert 5 == 6
test_sample1.py:6: AssertionError

这里在 test_sample1.py F.

F表示失败

点(.)表示成功。

在失败部分,您可以看到失败的方法和失败行。这里 x==y 表示 5==6 是假的。

接下来在这个 PyTest 教程中,我们将学习 PyTest 中的断言。

PyTest 中的断言

Pytest 断言是返回 True 或 False 状态的检查。在 Python Pytest 中,如果测试方法中的断言失败,则该方法的执行将在那里停止。该测试方法中的剩余代码不会执行,Pytest 断言将继续执行下一个测试方法。

Pytest 断言示例:

assert "hello" == "Hai" is an assertion failure.
assert 4==4 is a successful assertion
assert True is a successful assertion
assert False is an assertion failure.

考虑

assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

将此代码放在 test_file1_method1() 中而不是断言中

assert x == y,"test failed"

运行测试将导致失败为 AssertionError:test failed x=5 y=6

PyTest 如何识别测试文件和测试方法

默认情况下,pytest 只识别以 test_ 开头的文件名 或以 _test 结尾 作为测试文件。我们可以明确提及其他文件名(稍后解释)。 Pytest 要求测试方法名称以 “test 开头 。”即使我们明确要求运行这些方法,所有其他方法名称也会被忽略。

查看一些有效和无效 pytest 文件名的示例

test_login.py - valid
login_test.py - valid
testlogin.py -invalid
logintest.py -invalid

注意:是的,我们可以明确要求 pytest 选择 testlogin.py 和 logintest.py

查看一些有效和无效的pytest测试方法示例

def test_file1_method1(): - valid
def testfile1_method1(): - valid
def file1_method1(): - invalid	

注意:即使我们明确提到 file1_method1() pytest 也不会运行这个方法。

从特定文件和多个文件运行多个测试

目前,在文件夹 study_pytest 中,我们有一个文件 test_sample1.py。假设我们有多个文件,比如 test_sample2.py、test_sample3.py。要从文件夹和子文件夹中的所有文件运行所有测试,我们只需要运行 pytest 命令。

py.test

这将运行该文件夹和该文件夹下的子文件夹中所有以 test_ 开头的文件名和以 _test 结尾的文件名。

要仅从特定文件运行测试,我们可以使用 py.test

py.test test_sample1.py

使用 PyTest 运行整个测试的子集

有时我们不想运行整个测试套件。 Pytest 允许我们运行特定的测试。我们可以通过两种方式做到这一点

我们已经有了 test_sample1.py。创建一个文件 test_sample2.py 并将以下代码添加到其中

def test_file2_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
def test_file2_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

所以我们目前有

  • test_sample1.py
    • test_file1_method1()
    • test_file1_method2()
  • test_sample2.py
    • test_file2_method1()
    • test_file2_method2()

选项 1) 通过子字符串匹配运行测试

这里要运行名称中包含 method1 的所有测试,我们必须运行

py.test -k method1 -v
-k <expression> is used to represent the substring to match
-v increases the verbosity

所以运行 py.test -k method1 -v 会给你以下结果

test_sample2.py::test_file2_method1 FAILED
test_sample1.py::test_file1_method1 FAILED

============================================== FAILURES ==============================================
_________________________________________ test_file2_method1 _________________________________________
    def test_file2_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample2.py:5: AssertionError

_________________________________________ test_file1_method1 _________________________________________
    @pytest.mark.only
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample1.py:8: AssertionError

================================= 2 tests deselected by '-kmethod1' ==================================
=============================== 2 failed, 2 deselected in 0.02 seconds ===============================

在这里你可以看到接近尾声 2 个测试被“-kmethod1”取消选择 分别是 test_file1_method2 和 test_file2_method2

尝试使用各种组合运行,例如:-

py.test -k method -v - will run all the four methods
py.test -k methods -v – will not run any test as there is no test name matches the substring 'methods'

选项 2) 通过标记运行测试

Pytest 允许我们使用 pytest 标记 @pytest.mark 为测试方法设置各种属性。要在测试文件中使用标记,我们需要在测试文件上导入pytest。

在这里,我们将对测试方法应用不同的标记名称,并根据标记名称运行特定的测试。我们可以使用

定义每个测试名称上的标记
@pytest.mark.<name>.			

我们在测试方法上定义标记 set1 和 set2,我们将使用标记名称运行测试。使用以下代码更新测试文件

test_sample1.py

import pytest
@pytest.mark.set1
def test_file1_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

@pytest.mark.set2
def test_file1_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

test_sample2.py

import pytest
@pytest.mark.set1
def test_file2_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

@pytest.mark.set1
def test_file2_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

我们可以通过

运行标记的测试
py.test -m <name>
-m <name> mentions the marker name

运行 py.test -m set1。这将运行方法 test_file1_method1、test_file2_method1、test_file2_method2。

运行 py.test -m set2 将运行 test_file1_method2。

与 Pytest 并行运行测试

通常,一个测试套件将有多个测试文件和数百个测试方法,这将花费大量时间来执行。 Pytest 允许我们并行运行测试。

为此,我们需要首先通过运行安装 pytest-xdist

pip install pytest-xdist

你现在可以运行测试了

py.test -n 4

-n 使用多个工作人员运行测试。在上面的命令中,将有 4 个 worker 来运行测试。

Pytest 夹具

当我们想在每个测试方法之前运行一些代码时,会使用 Fixtures。因此,我们定义了fixture,而不是在每个测试中重复相同的代码。通常,fixture 用于初始化数据库连接、传递 base 等

一个方法通过标记标记为 Pytest 夹具

@pytest.fixture

测试方法可以通过将 Fixture 作为输入参数来使用 Pytest Fixture。

使用以下代码创建一个新文件 test_basic_fixture.py

import pytest
@pytest.fixture
def supply_AA_BB_CC():
	aa=25
	bb =35
	cc=45
	return [aa,bb,cc]

def test_comparewithAA(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

这里

每个测试函数都有一个输入参数,其名称与可用的夹具匹配。 Pytest 然后调用相应的夹具方法,返回的值将存储在输入参数中,这里是列表 [25,35,45]。现在列表项正在用于比较的测试方法中。

现在运行测试看看结果

 py.test test_basic_fixture
test_basic_fixture.py::test_comparewithAA FAILED                                                                                                                                                                                       
test_basic_fixture.py::test_comparewithBB PASSED                                                                                                                                                                                       
test_basic_fixture.py::test_comparewithCC FAILED
                                                                                                                                                                                       
============================================== FAILURES ==============================================
_________________________________________ test_comparewithAA _________________________________________
supply_AA_BB_CC = [25, 35, 45]
    def test_comparewithAA(supply_AA_BB_CC):
    	zz=35
>   	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"
E    AssertionError: aa and zz comparison failed
E    assert 25 == 35
test_basic_fixture.py:10: AssertionError

_________________________________________ test_comparewithCC _________________________________________
supply_AA_BB_CC = [25, 35, 45]
    def test_comparewithCC(supply_AA_BB_CC):
    	zz=35
>   	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
E    AssertionError: cc and zz comparison failed
E    assert 45 == 35
test_basic_fixture.py:16: AssertionError
================================= 2 failed, 1 passed in 0.05 seconds =================================

测试test_comparewithBB自zz=BB=35起通过,其余2次测试失败。

夹具方法仅在它定义的测试文件中具有范围。如果我们尝试访问其他测试文件中的夹具,我们将收到一个错误提示,提示夹具 'supply_AA_BB_CC' not found 用于其他文件中的测试方法。

要对多个测试文件使用相同的夹具,我们将在名为 conftest.py 的文件中创建夹具方法。

让我们通过下面的 PyTest 示例来看看这一点。使用以下代码创建3个文件conftest.py、test_basic_fixture.py、test_basic_fixture2.py

conftest.py

import pytest
@pytest.fixture
def supply_AA_BB_CC():
	aa=25
	bb =35
	cc=45
	return [aa,bb,cc]

test_basic_fixture.py

import pytest
def test_comparewithAA(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

test_basic_fixture2.py

import pytest
def test_comparewithAA_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

pytest 将首先在测试文件中查找fixture,如果没有找到,它将在conftest.py 中查找

通过 py.test -k test_comparewith -v 运行测试得到如下结果

test_basic_fixture.py::test_comparewithAA FAILED  
test_basic_fixture.py::test_comparewithBB PASSED 
test_basic_fixture.py::test_comparewithCC FAILED 
test_basic_fixture2.py::test_comparewithAA_file2 PASSED 
test_basic_fixture2.py::test_comparewithBB_file2 FAILED 
test_basic_fixture2.py::test_comparewithCC_file2 FAILED

Pytest 参数化测试

参数化测试的目的是针对多组参数运行测试。我们可以通过@pytest.mark.parametrize 做到这一点。

我们将在下面的 PyTest 示例中看到这一点。在这里,我们将 3 个参数传递给一个测试方法。此测试方法将添加前 2 个参数并将其与第 3 个参数进行比较。

使用以下代码创建测试文件 test_addition.py

import pytest
@pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
def test_add(input1, input2, output):
	assert input1+input2 == output,"failed"

这里测试方法接受 3 个参数——输入 1、输入 2、输出。它将 input1 和 input2 添加并与输出进行比较。

让我们通过 py.test -k test_add -v 运行测试,看看结果

test_addition.py::test_add[5-5-10] PASSED                                                                                                                                                                                              
test_addition.py::test_add[3-5-12] FAILED                                                                                                                                                                                              
============================================== FAILURES ==============================================
__________________________________________ test_add[3-5-12] __________________________________________
input1 = 3, input2 = 5, output = 12
    @pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
    def test_add(input1, input2, output):
>   	assert input1+input2 == output,"failed"
E    AssertionError: failed
E    assert (3 + 5) == 12
test_addition.py:5: AssertionError

您可以看到测试运行了 2 次——一次检查 5+5 ==10,另一次检查 3+5 ==12

test_addition.py::test_add[5-5-10] 通过

test_addition.py::test_add[3-5-12] 失败

Pytest Xfail / 跳过测试

在某些情况下,我们不想执行测试,或者测试用例与特定时间无关。在这些情况下,我们可以选择 Xfail 测试或跳过测试

xfailed 测试将​​被执行,但它不会被视为部分失败或通过测试。如果该测试失败,将不会显示回溯。我们可以使用 xfail 测试

@pytest.mark.xfail。

跳过测试意味着不会执行测试。我们可以使用

跳过测试

@pytest.mark.skip。

使用以下代码编辑 test_addition.py

import pytest
@pytest.mark.skip
def test_add_1():
	assert 100+200 == 400,"failed"

@pytest.mark.skip
def test_add_2():
	assert 100+200 == 300,"failed"

@pytest.mark.xfail
def test_add_3():
	assert 15+13 == 28,"failed"

@pytest.mark.xfail
def test_add_4():
	assert 15+13 == 100,"failed"

def test_add_5():
	assert 3+2 == 5,"failed"

def test_add_6():
	assert 3+2 == 6,"failed"

这里

通过py.test test_addition.py -v 执行测试,查看结果

test_addition.py::test_add_1 SKIPPED
test_addition.py::test_add_2 SKIPPED
test_addition.py::test_add_3 XPASS
test_addition.py::test_add_4 xfail
test_addition.py::test_add_5 PASSED
test_addition.py::test_add_6 FAILED

============================================== FAILURES ==============================================
_____________________________________________ test_add_6 _____________________________________________
    def test_add_6():
>   	assert 3+2 == 6,"failed"
E    AssertionError: failed
E    assert (3 + 2) == 6
test_addition.py:24: AssertionError

================ 1 failed, 1 passed, 2 skipped, 1 xfailed, 1 xpassed in 0.07 seconds =================

结果 XML

我们可以创建 XML 格式的测试结果,我们可以将其提供给持续集成服务器以进行进一步处理等。这可以通过

py.test test_sample1.py -v –junitxml=”result.xml”

result.xml 将记录测试执行结果。在下面找到一个示例 result.xml

<?xml version="1.0" encoding="UTF-8"?>
<testsuite errors="0" failures="1" name="pytest" skips="0" tests="2" time="0.046">
   <testcase classname="test_sample1" file="test_sample1.py" line="3" name="test_file1_method1" time="0.001384973526">
     <failure message="AssertionError:test failed because x=5 y=6 assert 5 ==6">
    @pytest.mark.set1
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
         test_sample1.py:9: AssertionError
    </failure>
   </testcase>
   <testcase classname="test_sample1" file="test_sample1.py" line="10" name="test_file1_method2" time="0.000830173492432" />
</testsuite>

我们可以看到一共有两个测试,其中一个失败。在下面,您可以在 标记下看到有关每个已执行测试的详细信息。

Pytest 框架测试 API

现在我们将创建一个小型 pytest 框架来测试 API。这里使用的 API 是来自 https://reqres.in/ 的免费 API。本网站只是提供可测试的 API。本网站不存储我们的数据。

这里我们会写一些测试

使用给定的代码创建以下文件

conftest.py - 有一个夹具,它将为所有测试方法提供基本 url

import pytest
@pytest.fixture
def supply_url():
	return "https://reqres.in/api"

test_list_user.py - 包含列出有效和无效用户的测试方法

import pytest
import requests
import json
@pytest.mark.parametrize("userid, firstname",[(1,"George"),(2,"Janet")])
def test_list_valid_user(supply_url,userid,firstname):
	url = supply_url + "/users/" + str(userid)
	resp = requests.get(url)
	j = json.loads(resp.text)
	assert resp.status_code == 200, resp.text
	assert j['data']['id'] == userid, resp.text
	assert j['data']['first_name'] == firstname, resp.text

def test_list_invaliduser(supply_url):
	url = supply_url + "/users/50"
	resp = requests.get(url)
	assert resp.status_code == 404, resp.text

test_login_user.py - 包含测试登录功能的测试方法。

import pytest
import requests
import json
def test_login_valid(supply_url):
	url = supply_url + "/login/" 
	data = {'email':'[email protected]','password':'something'}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 200, resp.text
	assert j['token'] == "QpwL5tke4Pnpja7X", resp.text

def test_login_no_password(supply_url):
	url = supply_url + "/login/" 
	data = {'email':'[email protected]'}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 400, resp.text
	assert j['error'] == "Missing password", resp.text

def test_login_no_email(supply_url):
	url = supply_url + "/login/" 
	data = {}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 400, resp.text
	assert j['error'] == "Missing email or username", resp.text

使用 py.test -v 运行测试

将结果视为

test_list_user.py::test_list_valid_user[1-George] PASSED                                                                                                                                                                               
test_list_user.py::test_list_valid_user[2-Janet] PASSED                                                                                                                                                                                
test_list_user.py::test_list_invaliduser PASSED                                                                                                                                                                                        
test_login_user.py::test_login_valid PASSED                                                                                                                                                                                            
test_login_user.py::test_login_no_password PASSED                                                                                                                                                                                      
test_login_user.py::test_login_no_email PASSED

更新测试并尝试各种输出

总结

在这个 PyTest 教程中,我们介绍了


Python

  1. 如何在 Google Cloud 上安装 WordPress
  2. 编码的 UI 测试自动化框架教程
  3. 什么是网络安全密钥?如何找到它?
  4. 什么是 WiFi 通话?它是如何工作的?
  5. 什么是 6G,它将有多快?
  6. 如何安装码头调平器
  7. 局部放电测试:它是什么以及它是如何工作的
  8. 什么是泵气蚀以及如何避免它?
  9. 什么是铜钎焊以及如何做?
  10. 什么是维护积压?如何克服它?
  11. 什么是真空管测试仪
  12. 如何测试液压泵?