I have a file that is saved in a particular format, and a class that will create an object based on the data in the file.
I want to ensure that all values in the file/string were extracted correctly by testing each attribute in the object.
Here is a simplified version of what I'm doing:
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
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
I want to create objects based on different data and use them as a parametrized value.
My current setup works, although there is the unnecessary overheard of creating the object for each test. I'd want them to be created once.
If I try doing the following:
...
@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
...
AttributeError: 'function' object has no attribute 'students'
...it doesn't work as it references the function rather than the fixture.
Furthermore, the code in test_name
and test_age
is almost identical. In my actual code, I'm doing this for about 12 attributes. Should/can this be merged into a single function? How?
How can I clean up my test code?
Thanks!
Edit:
I feel this is relevant, but I'm unsure how make it work for my situation: Can params passed to pytest fixture be passed in as a variable?
My current setup works, although there is the unnecessary overheard of creating the object for each test. I'd want them to be created once.
This smells like unnecessary pre-optimization to me, but if you care about this, then run the functions that create your data to test at module level, so they only run once.
For example:
...
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):
...
Furthermore, the code in test_name and test_age is almost identical. In my actual code, I'm doing this for about 12 attributes. Should/can this be merged into a single function? How?
How can I clean up my test code?
A couple of ways to do this, but from your example, provide an equality magic method to the Student
class and use that to test your code (also add a repr for sane representation of your object):
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)
Then your test can look like this:
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With