How to mock users and requests in django

54,969

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.

Share:
54,969

Related videos on Youtube

Purrell
Author by

Purrell

Updated on July 05, 2022

Comments

  • Purrell
    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
      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
      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
      Peter Rowell over 14 years
      I ... 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
    ozan over 14 years
    As Daniel mentioned, the test runner creates then destroys a test database for you, so you needn't worry about that.
  • Michael Williamson
    Michael Williamson over 14 years
    Except 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
    jlonganecker over 13 years
    The 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.
    TM. about 13 years
    Agreed, 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.
    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
    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.
    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
    user1066101 about 13 years
    @TM: Do you have any metrics that show that a mock is somehow faster than the built-in client?
  • TM.
    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
    Jonathan Hartley about 12 years
    My real requests have a .user attribute. Instances of django.http.HttpRequest do not. I'm just setting request.user after creating it. Does that seem reasonable?
  • AJJ
    AJJ over 11 years
    And 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
    Purrell over 9 years
    This 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
    garmoncheg over 9 years
    Best 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 have request.csrf_processing_done = True in the ready request (to pass the CSRF checks)
  • yarbelk
    yarbelk about 9 years
    While 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
    Manuel Carrero about 3 years
    Is't possible to define a key value as an empty list? I tried with post_request = rf.post('/submit/', {'foo': 'bar','myList':[]}) but with request.POST I got <QueryDict: {u'foo': [u'bar']}>