Django Testing: AttributeError: 'Client' object has no attribute 'get' - python

I am new to Django framework & I am trying write some tests for my apps in the project.Currently I have two apps hoardings & clients both have same basic CRUD features.For testing purpose I have created a test directory & it looks like this
clients
- tests
-__init__.py
- test_views.py
That's how I am maintaining my tests for both the apps.My test_views.py has following code,
from django.test import TestCase
from django.urls import reverse
from hoardings.models import State, City
from clients.models import Client
class ClientManagementTest(TestCase):
def setUp(self):
self.state = State.objects.create(desc='West Bengal')
self.city = City.objects.create(state=self.state, desc='Kolkata')
self.client = Client()
def test_client_creation_form_can_be_rendered(self):
response = self.client.get(reverse('clients:create'))
# Check that the response is 200 OK.
self.assertEqual(response.status_code, 200)
# check if csrf token is present
self.assertContains(response, 'csrfmiddlewaretoken')
# Check that the response contains a form.
self.assertContains(response, '<form')
# assert the context values
self.assertIn('url', response.context)
self.assertIn('heading', response.context)
self.assertIn('states', response.context)
self.assertIn('client_types', response.context)
As you can see in the setup method I am creating an object of Client which is used to send the request.But every time I run the tests I get following errors,
ERROR: test_client_creation_form_can_be_rendered
(tests.test_views.ClientManagementTest)
---------------------------------------------------------------------- Traceback (most recent call last): File
"/home/ropali/Development/PythonWorkspace/hms_venv/hms/clients/tests/test_views.py",
line 19, in test_client_creation_form_can_be_rendered response =
self.client.get(reverse('clients:create')) AttributeError: 'Client'
object has no attribute 'get'
As per my understanding It means that client object is not being created so it cannot find the get attribute & I get the similar error for the POST request as well.
But one thing is bugging me that I have similar test setup for the hoardings app it runs perfectly fine.
Can anyone please help me what I am doing wrong here.Let me know if you need other details.

Related

Flask session object does not persist between requests despite hardcoded secret key

I am currently running into an issue deploying a Flask app on Amazon's EB2 service. The Flask app works locally. When it is deployed, however, it only works for the first person who clicks the link. After that it throws the following error:
Internal Server Error The server encountered an internal error and was
unable to complete your request. Either the server is overloaded or
there is an error in the application.
The error it is throwing out concerns the Flask session - it becomes empty after routing from one site to another. I also noticed that the before_first_request function detailed below is ran only once, for the first user, and never again - which is even more bewildering.
Here's the minimal example:
from flask import Flask, render_template, request, session, url_for
application = Flask(__name__)
application.secret_key = "mysecretkey"
#application.before_first_request
def before_first_request():
""" these commands are run before the first request"""
# setup logging
application.logger.setLevel(logging.INFO)
application.logger.info('starting up Flask')
# clear session
session.clear()
# load in PID
session['pid'] = 123
# add parameters to the session
params = dict()
params['parameter'] = 0
session['params'] = params
application.logger.info(session) # it is printing the session as expected
return 'OK'
#application.route('/')
def main():
""" landing page """
application.logger.info(session) # empty
application.logger.info(application.secret_key) # as expected
params, results = session.pop('params'), session.pop('results') # throws out the error
return render_template('empty_template.jinja', args = session)
I am wondering if anyone might know what is going on how to resolve the issue?
I managed to solve it.
The error was that #before_first_request wrapper actually only ran once before first request ever made to the app. Hence, the session was actually only created and populated once.
I fixed that error by adding the call to before_first_request function at the top of the main function.

AttributeError: 'function' object has no attribute 'get' when testing Flask requests

Good afternoon,
I am developing some unit tests for my Flask application. It is my first time trying to develop unit tests for a Flask app, but I am currently getting this error when trying to test GET requests.
def testTagCategories():
> response = client.get("/forum")
E AttributeError: 'function' object has no attribute 'get'
I have been struggling to find what the problem is, as I have followed all the steps required in the Flask documentation. Here is the code.
#pytest.fixture(scope='session')
def test_client():
flask_app = app()
testing_client = flask_app.test_client()
ctx = flask_app.app_context()
ctx.push()
yield testing_client
ctx.pop()
#pytest.fixture()
def client(app):
return app.test_client()
#pytest.fixture()
def runner(app):
return app.test_cli_runner()
Finally, this is one of the functions where I get the error. Thanks in advance.
def test_access():
response = client.get("/")
assert (response.status_code == 200)
I'm also learning to unit test a flask controller so take my answer with a pinch of salt.
But in the failing code (like below) there appears to be no client instance and thus you cannot call the get method.
def test_access():
response = client.get("/")
assert (response.status_code == 200)
I'm sure there is a nicer way of doing it by having the client created before every test fixture (that's how I would do it in another language I'm more familiar with like C# with setup / teardown before each test).
But something like this works for me:
from app import application
def test_access():
client = application.test_client()
response = client.get('/')
assert response.status_code == 200
html = response.data.decode()
assert 'xxx' in html
in my app directory there is an __init__.py with application = Flask(__name__) in it. That's what from app import application is importing.
Would love to hear a better solution from someone though.
Edit: On the function that is causing an error, trying changing the signature to the following:
def test_access(client):

'flask instance' does not have attribute 'record' error

I have a project where I am trying to build an api using flask and python with the following structure:
graph:
-app.py
-server.py
-apis:
-__init__.py
-users.py
-transaction_functions.py
-neo4j_ops.py
In the server.py file I am trying to add authentification to the endpoints of my api which are coded in the users.py file. My server.py file looks like this:
import json
from six.moves.urllib.request import urlopen
from functools import wraps
from flask import Flask, request, jsonify, _request_ctx_stack
from flask_cors import cross_origin
from jose import jwt
AUTH0_DOMAIN = 'mydomain.eu'
API_AUDIENCE = 'https://my_audience.com'
ALGORITHMS = ["RS256"]
APP = Flask(__name__)
# Error handler
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
#APP.errorhandler(AuthError)
def handle_auth_error(ex):
#some code
# Format error response and append status code
def get_token_auth_header():
"""Obtains the Access Token from the Authorization Header
"""
# some code
return token
def requires_auth(f):
"""Determines if the Access Token is valid
"""
#wraps(f)
def decorated(*args, **kwargs):
#some code
return decorated
def requires_scope(required_scope):
"""Determines if the required scope is present in the Access Token
Args:
required_scope (str): The scope required to access the resource
"""
#some code
And I keep getting this error:
Traceback (most recent call last):
File "C:\Python37\lib\site-packages\flask_restplus\api.py", line 183, in init_app
app.record(self._deferred_blueprint_init)
AttributeError: 'Flask' object has no attribute 'record'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "app.py", line 16, in <module>
api.init_app(app)
File "C:\Python37\lib\site-packages\flask_restplus\api.py", line 186, in init_app
self._init_app(app)
File "C:\Python37\lib\site-packages\flask_restplus\api.py", line 204, in _init_app
self._register_view(app, resource, *urls, **kwargs)
File "C:\Python37\lib\site-packages\flask_restplus\api.py", line 282, in _register_view
resource_func = self.output(resource.as_view(endpoint, self, *resource_class_args,
AttributeError: 'function' object has no attribute 'as_view'
As you can see the result of this printstack is not really useful at all since none of these calls comes from any of my files.
The only file involved in that is app.py and it looks like this:
from flask import Flask
from flask_restplus import Api
from apis import api
import config
import os
app = Flask(__name__)
api.init_app(app)#traceback comes from here.
app.run(host='0.0.0.0', port=8080)
The apis/__init__.py file looks like this:
from flask_restplus import Api, fields
from .users import api as users
from flask import Flask
api = Api(
title='Graph Api',
version='0.2',
)
api.add_namespace(users)
Any idea of what the issue is?
If I import app (the flask instance) from app.py into server.py and use that flask instance created in app rather than creating a new whole flask instance in server.py somehow the error goes away, but the issue is that I would then create a circular call of dependencies, so I cant do that.
You already have an app
APP = Flask(__name__)
And your error handler at least is using that
Yet, you defined a second one
app = Flask(__name__)
And __name__ here is app (the name of the file), which is probably what's breaking things and why the server file isn't broken
The problem stopped existing as soon as I went back to the version of the code of before adding the server.py file, and then I added it again. I have no clue of what the issue was though as the code is actually the same.

AttributeError: 'TestSuite' object has no attribute 'client'

I'm writing Django unit test for Login form. Below is my sample code.
from unittest import TestCase
from django.contrib.auth.models import User
class TestSuite(TestCase):
def setUp(self):
self.credentials = {
'username': 'testuser',
'password': 'secret'}
User.objects.create_user(**self.credentials)
def test_login(self):
# send login data
response = self.client.post('/accounts/login', self.credentials, follow=True)
# should be logged in now
self.assertTrue(response.context['user'].is_active)
But when I'm executing from my console it's throwing the below error.
Traceback
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_login (accounts.tests.test_form.TestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Django\webapplication\accounts\tests\test_form.py", line 15, in test_login
response = self.client.post('/accounts/login', self.credentials, follow=True)
AttributeError: 'TestSuite' object has no attribute 'client'
----------------------------------------------------------------------
Ran 1 test in 0.502s
The problem is that python unittest module has not client in it; You should use django.test.
Simply change your first line as:
from django.test import TestCase
Read more about different test classes available to use.
You need django.test TestCase, instead of unittest's.

Setting (mocking) request headers for Flask app unit test

Does anyone know of a way to set (mock) the User-Agent of the request object provided by FLask (Werkzeug) during unit testing?
As it currently stands, when I attempt to obtain details such as the request.headers['User-Agent'] a KeyError is raised as the Flask test_client() doesn't set these up. (See partial stack trace below)
When attempting to get the User-Agent from the request object in a Flask project during unit testing, a KeyError is raised.
File "/Users/me/app/rest/app.py", line 515, in login
if request.headers['User-Agent']:
File "/Users/me/.virtualenvs/app/lib/python2.7/site-packages/werkzeug/datastructures.py", line 1229, in __getitem__
return self.environ['HTTP_' + key]
KeyError: 'HTTP_USER_AGENT'
-- UPDATE --
Along with the (accepted) solution below, the environ_base hint lead me to this other SO solution. The premise of this solution is to create a wrapper class for the Flask app and override the call method to automatically set the environment variables. This way, the variables are set for all calls. So, the solution I ended up implementing is creating this proxy class:
class FlaskTestClientProxy(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
environ['REMOTE_ADDR'] = environ.get('REMOTE_ADDR', '127.0.0.1')
environ['HTTP_USER_AGENT'] = environ.get('HTTP_USER_AGENT', 'Chrome')
return self.app(environ, start_response)
And then wrapping the WSGI container with that proxy:
app.wsgi_app = FlaskTestClientProxy(app.wsgi_app)
test_client = app.test_client()
You need to pass in environ_base when you call get() or post(). E.g.,
client = app.test_client()
response = client.get('/your/url/',
environ_base={'HTTP_USER_AGENT': 'Chrome, etc'})
Then your request.user_agent should be whatever you pass in, and you can access it via request.headers['User-Agent'].
See http://werkzeug.pocoo.org/docs/test/#testing-api for more info.
Even though what #chris-mckinnel wrote works, I wouldn't opt to use environ_base in this case.
You can easily do something as shown below:
with app.test_request_context('url', headers={'User-Agent': 'Your value'}):
pass
This should do the job and at the same time it keeps your code explicit - explicit is better than implicit.
If you want to know what you can pass to the test_request_context reference the EnvironBuilder definition; which can be found here.

Categories