Knowledge point 1:
1. Read the test result information
-
Passed means passed, there is an abbreviation.
-
failed means failure, there is an abbreviation F
2. Command line parameters
-
-h: help
-
-version: version information
3. Test case naming rules:
-
Test functions must start with test
-
A test class must start with Test, but a test class cannot have an init method
-
Test files must start with test/Test or end with test/Test
4. Run the specified test case: specified file [::class name][::function name]
-
By default, the rule of use case search is that pytest executes the current directory and subdirectories (traversal)
-
Specify the class name to run: pytest filename::classname
-
Specify the function name to run: pytest file name::class name::function name
-
Specify the function name to run: pytest filename::functionname
-
Run the file in the specified directory: pytest directory name\file name::function name
-
pytest .\test_a02\test_a02_a.py::test_001 (class test_001 under test_a02_a.py file; terminal)
5. pytest.main() execution use case
- Command line parameters args, list type, composed of one or more str, separated by commas
import pytest
def test_a01():
assert 1 == 1
if __name__ == '__main__': # 运行测试的话,需要在pycharm中设置python集成工具=>unittest
# pytest.main() # 等价于pytest命令
# pytest.main([__file__]) # 测试当前文件
# pytest.main(['--version'])
pytest.main['-sv', __file__] # 多参数调用
6. Run the specified test case - k parameter
-
Match the name part of the test class or test method
-
Case Sensitive
-
logic operation
expression | illustrate |
---|---|
-k login | Test the use case that contains login in the class name or function name |
-k not login | does not contain login |
-k login and success | Contains login and contains success |
-k login or success | Contains login or success |
-k login and not success | Contains login but does not contain success |
import pytest
def test_login_success():
pass
def test_login_failed():
pass
def test_logout_success():
pass
def test_logout_failed():
pass
if __name__ == '__main__':
pytest.main(['-k success and not failed', '--collect-only', __file__]) # –collect-only 含义:展示给定配置下哪些测试用例会被运行
7、pytest.mark.skip/skipif
- conditionally ignore skipif
import pytest, sys
@pytest.mark.skipif(1 == 1, reason='if 1==1 skip this') # 条件满足就忽略测试 reason:标注原因
def test_a01_1(reason='i wanna skip this case'):
pass
@pytest.mark.skipif(1 == 2, reason='if 1==2 skip this') # 条件不满足不会跳过
def test_a01_2():
pass
@pytest.mark.skipif(sys.platform == 'linux', reason='if linux skip this') # sys.platform 操作系统平台标识
def test_a01_3():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
- Output result s
import pytest
@pytest.mark.skip
def test_a01_1(reason='i wanna skip this case'):
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
# pytest.main(['--collect-only', __file__]) # 能被采集
# pytest.main([__file__]) # 输出s
import pytest
pytestmark = pytest.mark.skip('skip all cases') # pytestmark 这个名字不可更改
def test_a01_1():
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_1():
arrow = pytest.importorskip('arrow') # 这个库在当前环境下没有的话会报错,有会自动导入
nowtime = arrow.now().format('YYYY-MM-DD') # 获取当前时间
print(nowtime)
if __name__ == '__main__':
pytest.main(['-sv', __file__])
8. pytest.mark.parameterizeparameterization
-
When testing a test function, it is common to pass multiple sets of parameters to the function. For example, to test account login, we need to simulate all kinds of strange account passwords. Of course, we can write these parameters inside the test function for traversal. However, although there are many parameters, it is still a test. When a certain set of parameters causes the assertion to fail, the test is terminated. Through exception capture, we can ensure that all parameters are fully executed, but it takes a lot of extra work to analyze the test results
-
In pytest, we have a better solution, which is parameterized testing, that is, each set of parameters executes a test independently. The tool used is pytest.mark.parametrize(argnames, argvalues)
-
Analyze the source code of parameterize, and you can see the parameters to be used when calling
parameter | meaning |
---|---|
argnames |
Required, parameter names, comma-separated strings, indicating one or more parameter names, or a list or tuple of strings |
argvalues |
Required, a list of parameter values |
indirect | When it is True, argnames must be the function name of a fixture, and the value of argvalues is passed into the corresponding fixture, which is equivalent to @pytest.fixture(params=), and the default is False |
ids | Use case id, mark the execution name of the sub-use case, consistent with the number of argvalues, if not specified, it will be automatically generated, the default is None |
scope | The scope of action, similar to the scope parameter in the fixture, indicates the scope of the parameter, and the scope is used to group tests by parameter instances. It will override the scope defined by any fixture function, allowing dynamic scope to be set using the test context or configuration |
The parameters match, argnames must be consistent with the parameters of the test function, otherwise an error will be reported
import pytest
@pytest.mark.parametrize('arg0', [0, 1]) # (0, 1) '01' 不推荐用
def test_a01_1(arg0):
print(arg0)
if __name__ == '__main__':
pytest.main(['-sv', __file__])
multiple parameters
import pytest
@pytest.mark.parametrize('arg0, arg1', [(0, 1), (2, 2), (3, 4)]) # argvalues 往往是通过其他程序读取到
def test_a01_1(arg0, arg1):
print(f'arg0 is {
arg0},arg1 is {
arg1}')
assert arg0 == arg1
if __name__ == '__main__':
pytest.main(['-sv', __file__])
multiple decorators
import pytest
@pytest.mark.parametrize('arg0', [1, 2, 3]) # 多装饰器 笛卡尔积的效果 两两相乘
@pytest.mark.parametrize('arg1', [4, 5, 6])
def test_a01_1(arg0, arg1):
print(f'arg0 is {
arg0},arg1 is {
arg1}')
if __name__ == '__main__':
pytest.main(['-sv', __file__])
The role of the ids parameter
import pytest
def login(username, password):
data = {
'allen': '12', 'smith': '34', 'ford': '56'}
if data.get(username) == password:
return 'success'
else:
return 'failed'
@pytest.mark.parametrize('username, password, expect', [('allen', '12', 'success'), ('smith', '344', 'failed')],
ids=['login success', 'login failed'])
def test_a01_1(username, password, expect):
assert login(username, password) == expect
if __name__ == '__main__':
pytest.main(['-sv', __file__])
9. setup and teardown (not commonly used, there are better ones)
-
Setup often refers to the configuration part (preconditions) in the test case, and teardown corresponds to the destruction part (environment demolition)
-
PyTest supports xUnit style structure, setup() and teardown() methods are used to initialize and clean up the test environment, which can ensure the independence of test cases
method | scope | illustrate |
---|---|---|
setup_function/teardown_function | function function | Each function is used once (the function and method in the class are not counted) |
setup_class/teardown_class | class class | Need to be defined in the class, a class runs once |
setup_module/teardown_function | module (a py file) | run a module once |
setup_method/teardown_method | method (function in class) | one method run once |
setup/teardown | method (function in class) | one method run once |
import pytest
def setup_function():
print('\n函数前执行 setup')
def teardown_function():
print('\n函数后执行 teardown')
def test_a01_1():
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
class Test_all():
def setup_class(self):
print('\n方法前执行 setup')
def teardown_class(self):
print('\n方法后执行 teardown')
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_3():
pass
def setup_module():
print('\n模块前执行 setup')
def teardown_module():
print('\n模块后执行 teardown')
class Test_all():
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
class Test_all():
def setup_method(self):
print('\n方法前执行 setup01')
def teardown_method(self):
print('\n方法后执行 teardown01')
def setup(self):
print('\n方法前执行 setup02')
def teardown(self):
print('\n方法后执行 teardown02')
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
10. pytest-base-url plugin
import pytest, requests
def test_a01_1(base_url): # 当作为fixture使用的时候是base_url,安装的是pytest-base-url
resp = requests.get(base_url)
assert resp.status_code == 200
if __name__ == '__main__':
pytest.main(['-sv', '--base-url', 'https://www.baidu.com', __file__])
# pytest.main(['-sv', __file__]) # 使用配置文件执行,本级目录下新建pytest.init文件,新增一行 base_url = https://www.baidu.com
11. pytest-repeat plugin
import pytest
@pytest.mark.repeat(3)
def test_a01_1():
print('01 testing')
assert 1 == 1
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_1():
print('01 testing')
assert 1 == 1
if __name__ == '__main__':
pytest.main(['-sv','--count=3', __file__])
import pytest
def test_a01_A1():
print('A1 testing')
assert True
def test_a01_A2():
print('A2 testing')
assert True
def test_a01_B1():
print('B1 testing')
assert True
def test_a01_B2():
print('B2 testing')
assert True
if __name__ == '__main__':
pytest.main(['-sv', '--count=2', '--repeat-scope=session', __file__]) # session/module/class都是1212的顺序
12. Allure generates reports