3

I need a view that allows staff users to view objects in a draft state. But I'm finding it difficult to write a unittest for this view.

I'm using Factory Boy for my setup:

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    username = factory.LazyAttribute(lambda t: random_string())
    password = factory.PostGenerationMethodCall('set_password', 'mysecret')
    email = fuzzy.FuzzyText(
        length=12, suffix='@email.com').fuzz().lower()
    is_staff = True
    is_active = True

class ReleaseFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Release

    headline = factory.LazyAttribute(lambda t: random_string())
    slug = factory.LazyAttribute(lambda t: slugify(t.headline))
    author = factory.LazyAttribute(lambda t: random_string())
    excerpt = factory.LazyAttribute(lambda t: random_string())
    body = factory.LazyAttribute(lambda t: random_string())

class TestReleaseViews(TestCase):
    """
    Ensure our view returns a list of :model:`news.Release` objects.
    """

    def setUp(self):
        self.client = Client()
        self.user = UserFactory.create()
        self.client.login(username=self.user.username, password=self.user.password)

Given that I now have a logged-in, staff user for my tests, how do I go about using that to test against for a view (status_code 200 instead of 404)?

For instance, this test fails (404 != 200) when my view allows for users with is_staff as True to access the view:

def test_staff_can_view_draft_releases(self):
    "ReleaseDetail view should return correct status code"
    release = ReleaseFactory.create(status='draft')
    response = self.client.get(
        reverse(
            'news:release_detail',
            kwargs={
                'year': release.created.strftime('%Y'),
                'month': release.created.strftime('%b').lower(),
                'day': release.created.strftime('%d'),
                'slug': release.slug
            }
        )
    )
    self.assertEqual(response.status_code, 200)
Patrick Beeson
  • 1,667
  • 21
  • 36

2 Answers2

10

Actually, you receive a 404 error because the self.client.login call fails.

When you're passing password=self.user.password, you're sending the hash of the password, not the password itself.

When you call UserFactory(), the steps taken by factory_boy in your factory are:

  1. Create an object with {'username': "<random>", 'is_active': True, 'is_staff': True, 'email': "<fuzzed>@email.com"}
  2. save() it
  3. Call user.set_password('my_secret')
  4. Call user.save() again

By then, user.password is the result of set_password('my_secret'), not 'my_secret'.

I'd go for (in your test):

pwd = 'my_super_secret'
self.user = UserFactory(password=pwd)
self.client = Client()
self.assertTrue(self.client.login(username=self.user.username, password=pwd))

By the way, the declaration of your email field won't work as you expect it: when you write fuzzy.FuzzyText(...).fuzz().lower(), this gets executed only once, when the UserFactory class is declared.

You should instead use factory.fuzzy.FuzzyText(chars='abcdefghijklmnopqrstuvwxyz', length=12, suffix='@example.com').

Eric Palakovich Carr
  • 22,701
  • 8
  • 49
  • 54
Xelnor
  • 3,194
  • 12
  • 14
0

An alternative way than using the client, I have found it is helpful to instantiate the view and directly pass it the request.

self.request.user = UserFactory()
view = ReleaseView.as_view()
response = view(self.request)

And then you can

self.assertEqual(response.status_code, desired_status_code)

And even render the response if you so desire.

C.B.
  • 8,096
  • 5
  • 20
  • 34
  • Thanks! How would I use the client should I continue down this route? – Patrick Beeson Apr 18 '15 at 17:57
  • I would do a little debugging - do you intend to return a `403` if a user is not staff rather than `404`? I would check in the view why it would be failing and log it to stdout. Otherwise it looks like you have it setup correctly for factory boy. Does PostGeneration set the value of password correctly? What happens if you directly pass in the password rather than `user.password`? – C.B. Apr 18 '15 at 18:06