ill-logical's avatar

Should I test for unauthenticated behavior?

Hi, I am new to testing. So far, I have been testing each of my API endpoints for both authenticated and unauthenticated users:

public function testAuthenticatedUser()
{
    Passport::actingAs($this->user);

    $response = $this
        ->get('/api/posts')
        ->assertStatus(200);
        //... (other assertions)
}

public function testUnauthenticatedUser()
{
    $this
        ->json('GET', '/api/posts')
        ->assertStatus(401)
        ->assertExactJson([
            'error' => 'Unauthenticated.'
        ])
}

However, I ended up doing this on every controller method. My questions are:

  1. should I even be testing this? Am I not, instead of testing my app, in fact testing the Laravel framework?
  2. Should I abstract the behaviour? For instance, move the logic to the TestCase class?

How do you deal with this? Thank you for your answers.

0 likes
3 replies
duggie's avatar
duggie
Best Answer
Level 1

As with everything, it depends...

Do you want to sit in front of the Product Owner and say, with absolute confidence, that your API behaves correctly for authenticated AND non-authenticated users? When they "are you sure?", do you want to show them a bunch of tests which prove it? If so, your time has not been wasted. Your tests prove a requirement has been met.

What happens if someone accidentally removes the middleware declaration from the routes file, or removes your constructor? Will you have tests to catch this? You will if you test for authed and non-authed usage.

You've written them. They prove a requirement has been met. I'd say keep them in. :)

To give you an anecdote, I recently wrote an API which was protected by a "role" assigned to some users. My manual and automated tests showed this working fine locally. My PR went up to our build server and failed some automated tests. It turns out I had manually added the new role to my local DB during development. However, I had forgotten to create a seed file to add the new role. My permission-based tests caught that and I was very glad they did!

1 like
ill-logical's avatar

Thank you for your helpful comment, you are right I should keep the tests in.

However, now the question of abstraction emerges - suppose there are 500 controller methods altogether.

One way to refactor I can think of - move the 'unauthenticated' conditions to the parent class, so instead of:

public function testUnauthenticatedUser()
{
    $this
        ->json('GET', '/api/posts')
        ->assertStatus(401)
        ->assertExactJson([
            'error' => 'Unauthenticated.'
        ])
}

I would only write in my tests:

public function testUnauthenticatedUser()
{
    $this->mustFailAsUnauthenticated('GET', '/api/posts');
}

OK, however, I still have to reference the method in every test (remember, 500 times).

I guess my question is if there is a better way to abstract this.

Like having a dedicated 'unauthenticated' test, which would test all the API endpoints at once, and check that those behind the 'auth' middleware return 401.

I do not know if this is a common technique or even a good idea. If so, is there an example? I cannot quite imagine how I would programatically check all the API endpoints.

Thank you for your answer.

ill-logical's avatar

Update - I ended up extending the TestCase class (ApiEndpointTestCase).

That way I can have a trait for checking the User is unauthenticated. :)

1 like

Please or to participate in this conversation.