pythondjango-rest-frameworkdjango-webtest

How can I use Basic Authentication with django_webtest to access Django REST framework?


I'm having trouble posting data to some views that use Django REST framework in my tests. I'm using django_webtest to test my user API. I'm running into a problem with the following code:

class UserApiTest(WebTest):

    def setUp(self):
        AdminFactory()

    def test_accessing_user_list_shows_one_user(self):
        user_list = self.app.get('/quickstart/users/', user='admin')
        assert_that(user_list.json, has_entry('count', 1))

    def test_posting_new_user_returns_url_for_user_detail(self):
        post_data = {'username': 'john', 'email': 'john.doe@example.com'} 
        user_create = self.app.post('/quickstart/users/', post_data, user='admin')
        url = 'http://localhost:80/quickstart/users/2/'
        assert_that(user_create.json, has_entry('url', url))

The problem is that I get a CSRF error when the second test runs. Looking at the Django REST Framework documentation I read that the CSRF error is triggered only when using session-based authentication. So, I figured I'd try basic authentication, which according to Django's documentation only requires setting the REMOTE_USER environment variable:

class UserApiTest(WebTest):

    extra_environ = {'REMOTE_USER': 'admin'}

    def setUp(self):
        AdminFactory()

    def test_accessing_user_list_shows_one_user(self):
        user_list = self.app.get('/quickstart/users/')
        assert_that(user_list.json, has_entry('count', 1))

    def test_posting_new_user_returns_url_for_user_detail(self):
        post_data = {'username': 'john', 'email': 'john.doe@example.com'} 
        user_create = self.app.post('/quickstart/users/', post_data)
        url = 'http://localhost:80/quickstart/users/2/'
        assert_that(user_create.json, has_entry('url', url))

This worked worse, as the user wasn't even authorized to view these pages (i.e. accessing the URL's returned 403's).

My question is: How do I properly setup basic authentication with django_webtest?


Solution

  • After working on the problem, I found that I need to pass full basic authentication headers, instead of just the REMOTE_USER. This is because Django REST Framework parses the basic authentication in their BasicAuthentication backend.

    class UserApiTest(WebTest):                                                     
    
        auth_password = base64.encodestring('admin:default').strip()               
        extra_environ = {                                                           
            'AUTH_TYPE': 'Basic',                                                   
            'HTTP_AUTHORIZATION': 'Basic {}'.format(auth_password),                 
            'REMOTE_USER': 'admin'}                                                
    
        def setUp(self):                                                            
            AdminFactory()                                                          
    
        def test_accessing_user_list_shows_one_user(self):                          
            user_list = self.app.get('/quickstart/users/')                          
            assert_that(user_list.json, has_entry('count', 1))                      
    
        def test_posting_new_user_returns_url_for_user_detail(self):                
            post_data = {'username': 'john', 'email': 'john.doe@example.com'}     
            user_create = self.app.post('/quickstart/users/', post_data)            
            url = 'http://localhost:80/quickstart/users/2/'                         
            assert_that(user_create.json, has_entry('url', url))
    

    Also, by default, you should make sure any user you create in your tests has the is_staff flag set to yes. Of course, if you have set Django REST to have different permissions than you should make sure that those permissions are correctly set.

    I hope this helps someone.