How to mock users and requests in django
Solution 1
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)
Solution 2
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.
Solution 3
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.
Solution 4
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
Solution 5
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.
Related videos on Youtube
Purrell
Updated on July 05, 2022Comments
-
Purrell almost 2 years
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?
-
Mike DeSimone over 14 years"You keep using that word, but I do not think it means what you think it means..." I think you mean "mockup".
-
mpen over 14 years@Mike: It sounds funny, but I think he's got it right. @pax: Beat me to the punch-line :(
-
Peter Rowell over 14 yearsI ... I must confess that ... in the silence of my room ... late at night ... I ... yes, yes! I mock users! All of them! @perrierism: we're not making fun of you, we are just enjoying your wonderful choice of words.
-
ozan over 14 yearsAs Daniel mentioned, the test runner creates then destroys a test database for you, so you needn't worry about that.
-
Michael Williamson over 14 yearsExcept if you're using the database, then it's not a unit test any more. It may still be a perfectly valid integration test, but it's not a unit test.
-
jlonganecker over 13 yearsThe problem is that creating and destroying a database takes time. I want to eventually run thousands of tests in the blink of an eye, every time I make a change. I don't want to stand up a database and an application instance to run my tests.
-
TM. about 13 yearsAgreed, it's very useful to have tests that don't hit a DB. Even if you are using a SQLite database it's still wwaaaay slower than tests which use mocks.
-
TM. about 13 years@S.Lott it's good to use the real thing sometimes but it ends up very slow as your project grows. It's nice to have mock tests you can run in a few seconds rather than a few minutes.
-
user1066101 about 13 years@TM: Perhaps this is true in general. But the Django client is really fast. Do you have some alternative and some benchmarks to show the time savings?
-
TM. about 13 years@S.Lott just experience with my own projects. Alternative to using the built in user is just to use something like the pymox framework or creating your own mock objects with the same API as has been suggested in a few answers here. Using real request objects is not a speed issue.
-
user1066101 about 13 years@TM: Do you have any metrics that show that a mock is somehow faster than the built-in client?
-
TM. about 13 years@S.Lott I'm not saying the client is slow, or claiming mocks are faster than the client (I am assuming that by "client" you are referring to djangos dummy web "browser"). I'm saying they are faster than DB access (which only applies to the
User
portion of this question). If you don't think mocks are faster than actually accessing the DB, then I suspect you haven't actually tried and compared. On a very small project, our team went from test suite that took 1.5 minutes to < 5 seconds when we changed the code to mock out models. There's no need to write a benchmark when the gap is so big. -
Jonathan Hartley about 12 yearsMy real requests have a
.user
attribute. Instances ofdjango.http.HttpRequest
do not. I'm just setting request.user after creating it. Does that seem reasonable? -
AJJ over 11 yearsAnd you may need a META attribute as well with a few defaults set:
request.META = { 'SERVER_NAME': 'testserver', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.1', }
-
Purrell over 9 yearsThis is definitely the correct answer now that RequestFactory is available. With credit to Ozan's answer (that instantiating the real objects is sufficient and desirable). I haven't used Factory Boy but if it is close to the quality of Rail's Factory Girl it looks like it would be an excellent choice.
-
garmoncheg over 9 yearsBest solution that worked for me in a django migration script. However current version requires
request.user
to be set. Also to use this request like a normal view it is nice to haverequest.csrf_processing_done = True
in the ready request (to pass the CSRF checks) -
yarbelk about 9 yearsWhile I agree you can usually get away with creating Users for you tests - there are times you don't want to - or doing so is outside the remit of the test. If i'm testing that permission is denied if a helper method returns False - it is wrong for me to couple that to the database. That helper method exists BECAUSE I don't want to know about the database representation.
-
Manuel Carrero about 3 yearsIs't possible to define a key value as an empty list? I tried with
post_request = rf.post('/submit/', {'foo': 'bar','myList':[]})
but withrequest.POST
I got<QueryDict: {u'foo': [u'bar']}>