Jak*_*ake 5 python pytest python-3.x
我有一个以特定格式保存的文件,以及一个将根据文件中的数据创建对象的类.
我想通过测试对象中的每个属性来确保正确提取文件/字符串中的所有值.
这是我正在做的简化版本:
classlist.py
import re
class ClassList:
def __init__(self, data):
values = re.findall('name=(.*?)\$age=(.*?)\$', data)
self.students = [Student(name, int(age)) for name, age in values]
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
Run Code Online (Sandbox Code Playgroud)
test_classlist.py
import pytest
from classlist import ClassList
def single_data():
text = 'name=alex$age=20$'
return ClassList(text)
def double_data():
text = 'name=taylor$age=23$' \
'name=morgan$age=25$'
return ClassList(text)
@pytest.mark.parametrize('classinfo, expected', [
(single_data(), ['alex']),
(double_data(), ['taylor', 'morgan'])
])
def test_name(classinfo, expected):
result = [student.name for student in classinfo.students]
assert result == expected
@pytest.mark.parametrize('classinfo, expected', [
(single_data(), [20]),
(double_data(), [23, 25])
])
def test_age(classinfo, expected):
result = [student.age for student in classinfo.students]
assert result == expected
Run Code Online (Sandbox Code Playgroud)
我想基于不同的数据创建对象,并将它们用作参数化值.
我当前的设置有效,尽管不必要地为每个测试创建对象.我希望它们被创建一次.
如果我尝试执行以下操作:
...
@pytest.fixture(scope='module') # fixture added
def double_data():
text = 'name=taylor$age=23$' \
'name=morgan$age=25$'
return ClassList(text)
@pytest.mark.parametrize('classinfo, expected', [
(single_data, ['alex']),
(double_data, ['taylor', 'morgan']) # () removed
])
def test_name(classinfo, expected):
result = [student.name for student in classinfo.students]
assert result == expected
...
Run Code Online (Sandbox Code Playgroud)
AttributeError: 'function' object has no attribute 'students'
...它不起作用,因为它引用的是功能而不是夹具.
此外,代码中test_name和test_age几乎完全相同.在我的实际代码中,我正在为大约12个属性执行此操作.应该/可以将它合并为一个函数吗?怎么样?
我该如何清理我的测试代码?
谢谢!
编辑:
我觉得这是相关的,但我不确定如何使它适用于我的情况:传递给pytest fixture的params可以作为变量传入吗?
我当前的设置有效,尽管为每个测试创建对象是不必要的。我希望它们被创建一次。
对我来说,这听起来像是不必要的预优化,但如果您关心这一点,请运行创建数据的函数以在模块级别进行测试,因此它们只运行一次。
例如:
...
def single_data():
text = 'name=alex$age=20$'
return ClassList(text)
def double_data():
text = 'name=taylor$age=23$' \
'name=morgan$age=25$'
return ClassList(text)
double_data_object = double_data()
single_data_object = single_data()
@pytest.mark.parametrize('classinfo, expected', [
(single_data_object, ['alex']),
(double_data_object, ['taylor', 'morgan'])
])
def test_name(classinfo, expected):
result = [student.name for student in classinfo.students]
assert result == expected
@pytest.mark.parametrize('classinfo, expected', [
(single_data_object, [20]),
(double_data_object, [23, 25])
])
def test_age(classinfo, expected):
...
Run Code Online (Sandbox Code Playgroud)
此外,test_name 和 test_age 中的代码几乎相同。在我的实际代码中,我对大约 12 个属性执行此操作。应该/可以将其合并为一个函数吗?如何?
如何清理我的测试代码?
有几种方法可以做到这一点,但从您的示例中,为类提供一个相等魔术方法Student并使用它来测试您的代码(还添加一个代表您的对象的合理表示):
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return (self.name, self.age) == (other.name, other.age)
def __repr__(self):
return 'Student(name={}, age={})'.format(self.name, self.age)
Run Code Online (Sandbox Code Playgroud)
那么你的测试可能如下所示:
@pytest.mark.parametrize('classinfo, expected', [
(single_data(), [Student('alex', 20)]),
(double_data(), [Student('taylor', 23), Student('morgan', 25)]),
])
def test_student(classinfo, expected):
assert classinfo.students == expected
Run Code Online (Sandbox Code Playgroud)