I have a testing fixture to make a test client in my flask app:
@pytest.fixture(scope='session')
def test_client():
""" Create the application and the test client.
"""
print('----------Setup test client----------')
app = create_app(config_class=config.TestConfig)
testing_client = app.test_client()
ctx = app.test_request_context()
ctx.push()
yield testing_client # this is where the testing happens
print('-------Teardown test client--------')
ctx.pop()
I would like to add a header for "User-Agent" to this test request context because in my application I check the browser of the user. I am retrieving the browser name in my app via
user_agent = flask.request.user_agent.browser
This line returns None if I use the test client above. While I can successfully set the 'User-Agent' header in individual requests within a single test, e.g.:
user_agent_str = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
response = test_client.get('/some_flask_route',
environ_base={'HTTP_USER_AGENT': user_agent_str},
follow_redirects=True)
After doing this:
flask.request.user_agent.browser
returns 'chrome' as expected.
However, this is very redundant because it requires me to insert the environ_base line of code into every single one of my many tests.
I would expect that I could set the header when I make the test request context in my test fixture, e.g.:
user_agent_str = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
@pytest.fixture(scope='session')
def test_client():
""" Create the application and the test client.
"""
print('----------Setup test client----------')
app = create_app(config_class=config.TestConfig)
testing_client = app.test_client()
ctx = app.test_request_context(environ_base={'HTTP_USER_AGENT': user_agent_str})
ctx.push()
yield testing_client # this is where the testing happens
print('-------Teardown test client--------')
ctx.pop()
and this would set the environment for all requests, eliminating the need to set environ_base in every one of my requests.
While adding environ_base into test_request_context() does not break the fixture, it does not set the 'User-Agent' header and
flask.request.user_agent.browser
returns None.
I have tried the solution suggested in the question here: Setting (mocking) request headers for Flask app unit test
but it does not seem to change anything. I am simply running the flask server on localhost with no deployment yet, i.e. essentially just:
app = Flask(__name__)
app.run(host='127.0.0.1',port=5000,debug=True)
The test client has an environ_base
attribute that sets the default environ to start with when building each request.
c = app.test_client()
c.environ_base["HTTP_USER_AGENT"] = "Firefox/71.0"
For more control over what's passed to each request, FlaskClient
can be subclassed to override open
:
class CustomClient(FlaskClient):
def open(self, *args, **kwargs):
headers = kwargs.setdefault("headers", {})
headers.setdefault("User-Agent", "Firefox/71.0")
return super().open(*args, **kwargs)
app.test_client_class = CustomClient
c = app.test_client()
assert c.get("/browser") == "firefox"
Similarly, Flask
can be subclassed to override test_request_context
:
class CustomFlask(Flask):
def test_request_context(self, *args, **kwargs):
headers = kwargs.setdefault("headers", {})
headers.setdefault("User-Agent", "Firefox/71.0")
return super().test_request_context(*args, **kwargs)
app = CustomFlask()
with app.test_request_client():
assert browser() == "firefox"
Do not push a test request context globally for all the tests. A new context should be created for each request, and will be handled automatically when making tests requests using client
. Pushing one in a session fixture will not have the effect you want. In most cases you should use test_client
over test_request_context
.
Reconsider why you're depending on the user agent in the first place. It's usually bad design to base app behavior on the user agent, as browsers will misreport them and capabilities will change over time.