I have django code that interacts with request objects or user objects. For instance something like:
foo_model_instance = models.get_or_create_foo_from_user(request.user)
If you were going to test with the django python shell or in a unittest, what would you pass in there? Here simply a User object will do, but the need for a mock request object also comes up frequently.
For the shell or for unittests:
How do you mock users?
How do you mock requests?
For request, I would use RequestFactory included with Django.
from django.test.client import RequestFactory
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})
for users, I would use django.contrib.auth.models.User as #ozan suggested and maybe with factory boy for speed (with factory boy you can choose to not to save to DB)
How do you mock users?
Initialise a django.contrib.auth.models.User object. User.objects.create_user makes this easy.
How do you mock requests?
Initialise a django.http.HttpRequest object.
Of course, there are shortcuts depending on what you want to do. If you just need an object with a user attribute that points to a user, simply create something (anything) and give it that attribute.
You can either roll your own mocks, as Anurag Uniyal has suggested, or you can use a mocking framework.
In response to those saying you can just create an ordinary user as you would anyway in Django... I would suggest this defeats the point of the unit test. A unit test shouldn't touch the database, but by creating a user, you've changed the database, hence why we would want to mock one.
You don't need to mock Users, as you can just create one within your test - the database is destroyed after the test is finished.
To mock requests, use this snippet from Simon Willison.
Read about mock objects here
http://en.wikipedia.org/wiki/Mock_object
http://www.mockobjects.com/
And use this python lib to mock a user
http://python-mock.sourceforge.net/
else you can write a simple User class yourself, use this as a starting point
class MockUser(object):
def __call__(self, *args, **kwargs):
return self
def __getattr__(Self, name):
return self
add specfic cases etc etc
There are already a lot of good general answers. Here is a simple mock user used in tests involving admin forms:
class MockUser:
is_active = True
is_staff = True
def has_perm(self, *args):
return True
from django.test.client import RequestFactory
request = RequestFactory().get("/some/url")
request.user = MockUser()
Related
I am creating an application using Django. The application has user registration and login functionality.
I have three functions related to user authentication as of now, login, registration, and email_check (which is called when the user types email address to see if it is available). I was trying to group these functions under a class for better organisation and easy accessibility.
So I wrote a class and function like so:
class user_auth:
def check_email(email):
with connection.cursor() as conn:
conn.execute('select count(*) from user_info where email = %s', [email])
row = conn.fetchall()
response = bool(row[0][0])
return(response)
However, when I do this, I get a pylint error saying Method should have "self" as the first argument.
If I save this and call it as user_auth.check_email('abc#xyz.com'), it works just fine. But if I add self as the first argument, it stops working.
Am I using classes in an incorrect way? If yes, what is a better way to create a group of functions like this which can be easily imported using a single statement in other files?
As in the comments said, you could use a module for it.
Otherwise if you want to group them inside a class you just need to use the staticmethod or classmethod decorator.
Example:
class user_auth:
#classmethod
def check_email(cls, email):
...
#staticmethod
def static_check_email(email):
...
I'm building an application and one of the packages manage multiple auth methods.
Now it supports LDAP and PAM but I want in the future it supports a few more.
I have a package with
PAM.py and
LDAP.py
for example PAM.py contents:
import pam
class pam_auth:
def __init__(self, username=None, password=None):
self.username=username
self.password=password
def login(self):
res_auth=pam.authenticate(username=self.username, password=password)
return res_auth
and in another package I have the next class Login:
class Login:
def __init__(self,method=None):
self.authmethod=method
def login(self):
res_login=self.authmethod.login()
return res_login
Now i'm building my auth code like:
p=pam_auth()
p.username="pep"
p.password="just"
l=Login(method=p)
print l.login
And I believe that it is not the best way to do it, and thinking in multiples and different methods to auth.
For Example may be something like?:
l=Login(method=PAM.pam_auth)
l.username="pep"
l.password="just"
print l.login()
¿What is that I must change in Login Class or PAM class to work in this way?
For the change you mentioned, all you need to do is to instanciate the class inside Login's __init__:
class Login:
def __init__(self,method):
self.authmethod=method()
However, as Stefano Sanfilippo mentioned, this may actually hamper modularity, since suddenly Login must know the constructor parameters of the authentication method.
A couple more tips:
If you're writing python 2, you'll want to create new-style classes:
instead of
class Login:
use
class Login(object):
Also, if you're writing a general authentication layer, you probably don't want to deal explicitly with usernames and passwords: what will happen when you want to use third-factor, smartcard or biometric authentication in the future? You probably should deal with opaque "data", that the authentication method receives, unaltered.
In django, creating a User has a different and unique flow from the usual Model instance creation. You need to call create_user() which is a method of BaseUserManager.
Since django REST framework's flow is to do restore_object() and then save_object(), it's not possible to simply create Users using a ModelSerializer in a generic create API endpoint, without hacking you way through.
What would be a clean way to solve this? or at least get it working using django's built-in piping?
Edit:
Important to note that what's specifically not working is that once you try to authenticate the created user instance using django.contrib.auth.authenticate it fails if the instance was simply created using User.objects.create() and not .create_user().
Eventually I've overridden the serializer's restore_object method and made sure that the password being sent is then processes using instance.set_password(password), like so:
def restore_object(self, attrs, instance=None):
if not instance:
instance = super(RegisterationSerializer, self).restore_object(attrs, instance)
instance.set_password(attrs.get('password'))
return instance
Thanks everyone for help!
Another way to fix this is to overwrite pre_save(self, obj) method in your extension of viewsets.GenericViewSet like so:
def pre_save(self, obj):
""" We have to encode the password in the user object that will be
saved before saving it.
"""
viewsets.GenericViewSet.pre_save(self, obj)
# Password is raw right now, so set it properly (encoded password will
# overwrite the raw one then).
obj.user.set_password(obj.user.password)
Edit:
Note that the obj in the code above contains the instance of User class. If you use Django's user model class directly, replace obj.user with obj in the code (the last line in 2 places).
I'm working with DRF. And here is how I create users:
I have a Serializer with overrided save method:
def save(self, **kwargs ):
try:
user = create_new_user(self.init_data)
except UserDataValidationError as e:
raise FormValidationFailed(e.form)
self.object = user.user_profile
return self.object
create_new_user is just my function for user creation and in the view, I just have:
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
It seems like you should be overriding restore_object() in your serializer, not save(). This will allow you to create your object correctly.
However, it looks like you are trying to abuse the framework -- you are trying to make a single create() create two objects (the user and the profile). I am no DRF expert, but I suspect this may cause some problems.
You would probably do better by using a custom user model (which would also include the profile in the same object).
I want to test
if a views decorator works properly
if a proper view is called
So, here's decorator get_object and view features are in myapp.views.
#get_object
def features(request, object):
return {}
I try mocking this:
new_view = Mock(__name__='features', return_value={})
decorated = get_object(new_view)
with patch('myapp.views.features') as features:
features = decorated
client = Client()
response = client.get('/features')
print new_view.call_args
This shows nothing, as if the mock object was not called, although I suppose it should have been.
How can I mock the decorated view properly?
When you call patch you are replacing your view with a brand new MagicMock. Since you are manually constructing a mock for you view you should do something more like (untested):
new_view = Mock(__name__='features', return_value={})
features = get_object(new_view)
with patch('myapp.views.features', features):
client = Client()
response = client.get('/features')
print new_view.call_args
This will replace myapp.views.features with your hand rolled mock.
In general though I would recommend that you test your decorator in isolation without using the Django test client. Your really mixing two tests into one here. The first is making sure the decorator does it's duty and the other is making sure the request is properly routed.
In the code below the User class needs to access a function get_user inside an instance of WebService class, as that contains other functions required for authentication with the web server (last.fm). Actual code is here.
class WebService:
def __init__(self, key):
self.apikey = key
def get_user(self, name):
pass # Omitted
class User:
def __init__(self, name, webservice):
self.name = name
self.ws = webservice
def fill_profile(self):
data = self.ws.GetUser(self.name)
# Omitted
The problem is that a reference needs to be held inside every ´User´. Is there another way of doing this? Or is it just me overcomplicating things, and this is how it actually works in the real world?
As requested:
As to handling things like get_top_albums and get_friends, that depends on how you want to model the system. If you don't want to cache the data locally, I'd say just call the service each time with a user ID. If you do want to cache the data locally, you could pass a User object to the method in WebService, then have the method populate the members of the User. You do have to make a design decision though to either have a WebService and a User (what would probably be best), or just a UserWebService.
You can certainly make the reference a static variable, if the web service object is the same for all users.
The syntax is:
class User:
webservice = ...
...
You will then even be able to access it from User instances, but not to assign to it that way, that would require User.webservice syntax.
You are also getting good design alternatives suggested in the comments.