14

I'm new to Pytest. I want to test my views which require login (decorated with @login_required).
I have following test function:

def test_add_new_post(self, client, user):
    login_user(user)
    assert current_user == user
    data = {
        'title': 'This is test post',
        'body': 'This is test body'
    }
    client.post(url_for('posts.add_new'), data=data)
    assert Post.query.count() == 1

where the client is:

@pytest.fixture(scope='session')
def client(request, app):
    return app.test_client()

The assert current_user == user returns True, but the client.post returns the login page, because the login_required redirects to a login page. Why is this happening and what is the correct way of doing this?

4 Answers 4

6

This worked for me (based on this comment):

def test_with_authenticated_user(app):
    @app.login_manager.request_loader
    def load_user_from_request(request):
        return User.query.first()
    # now you can call client.post or similar methods
Sign up to request clarification or add additional context in comments.

Comments

4

perhaps not the most streamlined way, but: "It can be convenient to globally turn off authentication when unit testing. To enable this, if either of the application configuration variables LOGIN_DISABLED or TESTING is set to True, this decorator will be ignored." via https://pythonhosted.org/Flask-Security/api.html

Comments

0

What worked for me was modifying the client fixture so that you log in the user and return the client inside of a context.

For example, in your conftest.py file:

from app.models import User # change according to where your User class is
from flask_login import login_user

@pytest.fixture(module='session')
def client(app):
    with app.test_request_context():
        user = User(name='test', email='[email protected]', ...)
        login_user(user)
        yield app.test_client()

Comments

0

For anyone who landed here trying to test routes that require user login: sending a POST request to the login route before running the actual test worked for me:

import pytest
from your_project import app, current_user, login_manager

# Configure app
app.config.update({"TESTING": True})  # Set TESTING to True
app.config.update({"WTF_CSRF_ENABLED": False})  # Set csrf token to False
app.config.update({"LOGIN_DISABLED": True})  # Disable login

# Initiate login_manager
login_manager.init_app(app)
login_manager.login_view = "login"

# Mock user loader
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
        
def test_foo():
    # Use app context
    with app.test_client() as client: 
        # First, login the user via the `/login` route
        response = client.post('/login', data={
            'username': TESTUSER,
            'password': TESTPASSWORD
        }, follow_redirects=True)

        # Check if the login was successful
        assert response.status_code == 200
        assert current_user.is_authenticated
        
        # Test the POST request for the `/foo` route
        response = client.get(path='/foo', 
                              follow_redirects=False)
    # Assert the server responded OK status
    assert response.status_code == 200
    # Add further tests specific to the `/foo` route

Granted, this does not fix the login_user() issue but for me the latter just didn't work with my code - even after three hours of trying and fixing various parts. In the end, all I needed was to have a mock logged-in user and the code above solved it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.