Home > Back-end >  Pytest - reuse fixture that outputs differently for different users
Pytest - reuse fixture that outputs differently for different users

Time:07-18

I have three test user fixtures defined as below in conftest.py

@pytest.fixture(scope="session")
def user1(db: Session):
    user_create = UserCreate(name="u1", email="[email protected]", password="pass")
    return Users.create(db=db, user_create=user_create)


@pytest.fixture(scope="session")
def user2(db: Session):
    user_create = UserCreate(name="u2", email="[email protected]", password="pass")
    return Users.create(db=db, user_create=user_create)


@pytest.fixture(scope="session")
def user3(db: Session):
    user_create = UserCreate(name="u3", email="[email protected]", password="pass")
    return Users.create(db=db, user_create=user_create)

and I have a utility function that generates the Authorization Bearer Token for a logged in user after being provided the credentials:

def user_auth_test_token(
    client: TestClient, db: Session, email: EmailStr, name: str, password: str
):
    user = Users.get(db=db, email=email)
    if not user:
        user = Users.create(
            db=db, user_create=UserCreate(name=name, email=email, password=password)
        )

    return _user_auth_header(client=client, email=email, password=password)

I have multiple FastAPI endpoints which are authenticated, and needs to be supplied with the right auth header. And there are certain test cases for which I need simultaneous login from multiple users. Can I create a parameterized fixture user_token that can be provided with the corresponding user fixture (i.e., one of the 3 above) that can generate the appropriate auth header that can be used in the test?

At the moment I have to manually call the user_auth_test_token() for each authenticated endpoint testing and fill it up like this:

def test_auth(client: TestClient, user1: Users):
    header = user_auth_test_token(client=client, db=db, email=user1.email, name=user1.name, password='pass')
    r = client.post(f"{settings.API_STR}/users/me", header=header)
    ...

This is too much boilerplate. In order to access auth-endpoints for multiple users I have to write the same procedure multiple times, which is cumbersome. Is there a better way?

CodePudding user response:

I can think of two ways but I'm not sure which option would better suit your needs:

1) Test function parametrization

This way you would have the user token/header ready from the beginning but the way I present it, it doesn't support multiple users at once but it does support multiple invocations of the test function for different users.

@pytest.fixture()
def user_token(client: TestClient, db: Session, user1: Users, user2: Users, user3: Users, request):
    user = {'user1': user1, 'user2': user2, 'user3': user3}.get(request.param[0])

    return user_auth_test_token(client=client, db=db, email=user.email, name=user.name, password=request.param[1])

@pytest.mark.parametrize('user_token', [('user1', 'pass')], indirect=True)
def test_auth(user_token):
    r = client.post(f"{settings.API_STR}/users/me", header=user_token)

2) Fixture returning a function

This is somewhat more flexible because inside your test function you could call user_token multiple times with different users.

from typing import Callable

@pytest.fixture()
def user_token(client: TestClient, db: Session):
    def _(user: Users, password: str):
        return user_auth_test_token(client=client, db=db, email=user.email, name=user.name, password=password)
    
    return _

def test_auth(user_token: Callable, user1: Users):
    r = client.post(f"{settings.API_STR}/users/me", header=user_token(user=user1, password='pass'))
    ...
  • Related