1

I'm quite new to factory-boy and I'm trying to send a request to an API endpoint in my unit test, which requires a user to be authenticated. The endpoint expects a token in the header in the form of 'Bearer ' + token. I've looked at a few examples online and this is what I've come up with so far in my unit test:

test_user.py

class UserFactory(factory.Factory):
    class Meta:
        model = user

    username = factory.LazyAttribute(lambda t: "myuser")
    password = factory.PostGenerationMethodCall('set_password', 'my_super_secret')
    is_staff = True
    is_active = True

class UserViewSetTest(TestCase):
    def setUp(self):
        pwd = 'my_super_secret'
        self.user = UserFactory(password=pwd)
        self.client = Client()
        self.assertTrue(self.client.login(username=self.user.username, password=pwd))

    def test_user_list(self):
        response = self.client.get(reverse('user', kwargs={'fromdate': '2017-01-01', 'todate': '2017-04-01'})), format='json')
        self.assertEqual(response.status_code, 200)

The initial error is the fact that this assertion self.assertTrue(self.client.login(username=self.user.username, password=pwd)) is false so the test fails right away. Even if I remove that line, the API call returns a 401 because the authentication isn't successful.

How can I successfully authenticate a user in this API call with factory-boy so that I can send a token in the API request? Can I use a user model provided by the framework?

EDIT:

I've tried to create a token in order to pass it through the header as such:

def setUp(self):
    self.user = UserFactory.create()
    self.factory = APIRequestFactory()
    self.token = Token.objects.create(user=self.user)
    self.token.save()

def test_user_list(self):
    self.client = APIClient()
    self.client.credentials(HTTP-AUTHORIZATION='Bearer ' + self.token.key)
    response = self.client.get(reverse('user', kwargs={'fromdate': '2017-01-01', 'todate': '2017-04-01'})), format='json')
    self.assertEqual(response.status_code, 200)

However, I'm still getting an

AssertionError: 401 != 200

I've also seen that there is a force_authentication method but I'm not sure how to use it. Any help is appreciated.

Adam
  • 2,384
  • 7
  • 29
  • 66

2 Answers2

5

You're using factory.Factory instead of factory.django.DjangoModelFactory.

factory.Factory doesn't automatically save to the db, so you can either switch to DjangoModelFactory, or run self.user.save() manually

You also don't need self.client = Client(), as self.client already exists

Tim Nyborg
  • 1,599
  • 1
  • 9
  • 16
  • Thank you for responding. I've tried your suggestions. However, if I replace `factory.Factory` with `DjangoModelFactory`, I get an error `django.db.utils.OperationalError: no such table: auth_user`. If I keep `factory.Factory` and add `self.user.save()`, I get an error: `AttributeError: 'Client' object has no attribute 'save'` – Adam May 31 '21 at 20:50
  • Also, the reason I'm using `factory.Factory` instead of `factory.django.DjangoModelFactory` is from this: https://stackoverflow.com/a/26244552/3480297 – Adam May 31 '21 at 20:56
  • Sounds like you have a separate issue: ensuring your test database & migrations create the required tables. Saving and using a User record will never work if auth_user (or a custom user model) isn't first created. – Tim Nyborg May 31 '21 at 21:33
  • Could you advise what I should do in this case? Alternatively, is there a tutorial I can follow that you'd recommend to get this setup to work? – Adam May 31 '21 at 21:38
2

I have forced the authentication in the following way. I'm not sure this is the best practice as it seems to be bypassing the issue rather than resolving it, so if anyone else has other ideas, please let me know.

view = views.{VIEWSET_ENDPOINT}.as_view({'get': 'list'})
request = self.factory.get('{ENDPOINT}')
force_authenticate(request, user=self.user)
response = view(request)
Adam
  • 2,384
  • 7
  • 29
  • 66