I'm working on a user search feature and would like some feedback about the best way to structure the creation of test data using pytest. Below I have defined a test file, test_basic.py, that defines fixture functions to create users and add them to my "database" of users:
@pytest.fixture
def user_alice():
alice = User('Alice')
users.append(alice)
return alice
Tests set the state of the user database by requesting user objects as parameters, which will trigger the corresponding pytest fixture functions. Here is a test that shows the meat of this: the test requests the user Alice, which is created by the fixture and can be used to test the search function:
def test_can_find_alice_by_name(user_alice):
assert user_alice in search('Alice')
I have tried to strip these examples down to the bare essentials, but even in the simple case of test_basic.py there is a lot of repetition to set up each user. As I expand the user fields I am able to search, I'll want to create more users and more combinations users, which means adding more fixture functions, all of which do the same thing with relatively minor variations in the data.
I'd appreciate your feedback on my proposed solution below, which defines a fixture factory that knows how to create users. The idea is this: for each test, we look to see if the test is requesting any users by checking its parameters for names that start with user_
. If we find any, create the requested user object and pass it back to the test:
# excerpt from test_factory.py; factory fixture replaces _all_ the user
# fixtures in test_basic
#
# Factory that creates Users requested by a test case. Test cases request a
# User by specifying a parameter name like 'user_<slug>'. The User instance
# is created and passed back to the test using the requested name. Any number
# of Users may be requested this way.
#
@pytest.fixture(autouse=True)
def user_factory_fixture(request):
# We only want test case parameters that start with 'user_'
_users = filter(lambda f: f.startswith('user_'), request.funcargnames)
for user_slug in _users:
# Create the user
name = user_slug.split('_')[1].capitalize()
user = User(name)
users.append(user)
# Pass the user back to the test
request._funcargs[user_slug] = user
I appreciate any thoughts, suggestions, or advice you might have about this problem generally. While I'm using search as an example, I have run into this same problem in other contexts and am trying to learn the best practices for test environment and data management.
Telling me "you're going about this the completely wrong way" is a perfectly legitimate answer if you are willing to tell me why! :-)