3

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:

  • Requires hardcoding credentials in plaintext somewhere (my app uses social media log in so there are no test or made-up credentials available)
  • Introduces a dependency; login routine must now be run in a test or fixture and no other tests can be run in isolation of it
  • Slow; must be run before every test as a browser action

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:

  • PITA; Many times I will forget to do this, causing me to have to rerun tests. Because it's such a hassle I may be motivated to run the test suite less.
  • Not scalable; if I ever tried testing multiple browsers or on a different environment this is right out the window as an option
  • Slow; still requires Selenium to click through login page and hit 'Authorize app' on a third-party site.

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?

10
  • Can you add the code for your login page of your Flask server and a simple test page? Commented Jan 30, 2017 at 14:38
  • @MaximilianPeters The login is quite similar to this, which I'm pretty sure I used as a reference. I'm not sure what you mean by a simple test page. It seems like you're interested in showing me how to have Selenium get the form elements and perform a scripted login, which does not work for me (see Unacceptable Solution 1). Commented Jan 30, 2017 at 15:05
  • Can you extract a cookie after login with any tool, and then add them to selenium? Another variant: Se doesn't try to fill a form, but posts to hardcoded URL hardcoded values to obtain a session cookie. Commented Jan 31, 2017 at 11:39
  • @EugeneLisitsky I thought about the cookie extraction but I have no idea how to get a valid one with this setup. Post it as an answer if you know how. Commented Jan 31, 2017 at 20:21
  • For LOGIN_DISABLED to work it needs to be set True in app.config prior to initializing flask-login. It would apply when your endpoints are protected with login_required or fresh_login_required. Since I used roles_required from flask-security instead, I ended up triggering the identity in a before_request hook. How are your resources protected? Perhaps you can show an example. Commented Feb 1, 2017 at 11:58

4 Answers 4

2
+300

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)

Sign up to request clarification or add additional context in comments.

3 Comments

Have you tried PhantomJS? In one of my questions there's a recipe to obtain cookies in pure client-side.
It's become clear to me that some of my criteria are basically impossible and I'm not going to be able to do this the way I'd prefer. I'm giving you the bounty for presenting the fastest solution and helping me avoid the most costs in the implementation. Thanks for all your work tracking this stuff down and the alternate suggestions.
Thank you. Pls feel free to ask, I'm glad to help.
1

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.

Comments

1

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()

1 Comment

@Two-BitAlchemist I'm curious if this worked for you. Perhaps you've moved on to other things.
0

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.

1 Comment

I may have to go this way. I'm quickly losing hope I can log in without storing and using real social media credentials. Ugh.

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.