Two-Bit Alchemist
Two-Bit Alchemist

Reputation: 18477

Run Selenium Tests on Flask App with Logged In User

I am building a Flask-backed web app where all the interesting pages are behind a login. I would like to run automated tests against it using the Selenium WebDriver. I cannot seem to figure out how to log in a user and associate it with Selenium. Everything I try results in Selenium being presented with the "Please log in" page.

Unacceptable Solution 1: Scripted Selenium Log In

Many of the resources I have seen (1, 2, 3, among many others) suggest manually logging in with a scripted test, which is to say navigating Selenium to the log in page, filling out forms, and clicking buttons. This is a terrible solution for many reasons including but not limited to:

Unacceptable Solution 2: Require Manual Log In Before Selenium Tests Run

Something else I could do is open the browser I'm targeting myself before running the Selenium tests and log in. This keeps the credentials out of Selenium's hands. Problems:

Failed Attempted Solution 1: Session Fakery

Using something like this:

with client.session_transaction() as session:
    session['user_id'] = test_user.id
    session['_fresh'] = True

where client is an instance of app.test_client(). (This is in a pytest fixture; I'm using Flask_login). This allows me to log in during regular unit tests, but I have not been able to figure out how I could get Selenium to use this same session.

Failed Attempted Solution 2: login_user

Similar to the above I can try calling Flask_login.login_user(test_user) in a fixture, but I have not successfully been able to associate Selenium with this user. Since the session already failed, the only thing I could think would be to set a cookie for this logged in user on Selenium driver.set_cookie, but I have no idea how to get a proper one out of the test client or Flask login to do so.

Failed Solution #3: LOGIN_DISABLED

According to the Flask-Login docs you can set LOGIN_DISABLED in your app config to bypass the login_required decorator. I did this but I'm still seeing a login page.

I dug into this a bit more and the issue is not that LOGIN_DISABLED isn't working but that a lot of views depend on the behavior of Flask_login's current_user.is_anonymous. If I were going to go this route I'd also need a solution to make sure that current_user is set to the test user, so that actions in the app are correctly grouped with the user that's "logged in".

How do I authenticate once without using Selenium and then get Selenium to use that authenticated user for the rest of its session?

Upvotes: 3

Views: 2486

Answers (4)

user650881
user650881

Reputation: 2505

You can create an AuthenticatedUser and make it the current_user before the tests.

In my case I use flask-security with Roles so I also create and embed a specific Role and call hooks so it integrates with flask-principal and overrides the user-lookup that occurs in flask-security.

Since you are seemingly not using these extra libraries I will try to recraft it to your case. When you want to disable login you would call what I have named force_authenticated(app) before your first request occurs. (It will error if called after requests have already occurred.)

# This is a non-anonymous, authenticated user
class AuthenticatedUser(flask_login.UserMixin):
    def get_id(self):
        return 'TestUser'

def force_authenticated(app):
    @app.before_request
    def set_identity():
        user = AuthenticatedUser()
        flask_login.login_user(user)

Here is a full, really minimal, confirmed-working example:

from flask import Flask
import flask_login

class AuthenticatedUser(flask_login.UserMixin):
    def get_id(self):
        return 'TestUser'

def force_authenticated(app):
    @app.before_request
    def set_identity():
        user = AuthenticatedUser()
        flask_login.login_user(user)

app = Flask(__name__)
app.secret_key = 'blahblah'

lm = flask_login.LoginManager(app)
lm.init_app(app)

force_authenticated(app)

@app.route('/')
@flask_login.login_required
def hello_world():
    return 'Hello, World!'

app.run()

Upvotes: 1

Eugene Lisitsky
Eugene Lisitsky

Reputation: 12875

How to get cookies after login. One can simulate login process, find you form take action attribute from it and names of fields for username and password. Look what cookie is used for storing session key, suppose it is sid.

get_cookie.py:

import requests
r = requests.post(action, 
                  data={'login': 'your_username', 'password': 'You_p@ssw0rd'}, 
                  allow_redirects=False)
# now r.cookies contains all returned cookies,
# print cookie you need:
print(r.cookie['sid'])

Try to do it in a webdriver session initialisation. Or you may run it and feed this cookie to Selenium, for example as a command line parameter (just use your correct parameters):

selenium .... --session=$(./get_cookie.py)

Upvotes: 2

Ujjaval Moradiya
Ujjaval Moradiya

Reputation: 222

Cam you please read following link ?

Login using selenium

In this, login is done using selenium and cookie is stored in a file for the later use. And after that you can use requests with the saved cookie to access other pages.

Upvotes: 1

Jeff Campbell
Jeff Campbell

Reputation: 1

We have a form just like what you are using flask. What we have done is create a default configuration file in the GIT project. Then I pulled this down to my box and created my own configuration file from this default and put my credentials in this file. It seems to work fine for us. I run all my tests from Eclipse using pydev. I recently started using the HTMLTestRunner to get a better picture of results. I hope this helps you out.

Upvotes: 0

Related Questions