phplaravellaravel-5jwtphpunit

Laravel 5.3 PHPUnit Test Service Provider Persisted Between Requests


I'm using JWTAuth tokens for authentication. In the tests I've been passing the token up on each request after receiving it upon authentication, but when a few specific tests needed to be run on a set of different users in a loop the tests that worked for single users began to fail, which didn't make sense.

Through logging I can see that regardless that I'm invoking logout the service provider that I use on each request to get the authenticated user is only constructed on the first request and then appears to persist. So even though the user was logged out and a new user was logged in with and has a new token the service provider is not re-instantiated on each request like it is in the production application.

Is there a way to have the service provider not persist or be stored between requests? I noticed through debugging that I didn't even need to pass a token after login to the server on subsequent authenticated routes since it just reuses the persisted data in the service provider.

The example below works for any single user, but when iterated over multiple users the rule that a single user can't have two open applications throws and indicates the first user is still being used on the second iteration.

public function applicant_can_prequalify_if_age_of_majority_for_province_or_older()
{
    // Arrange
    $this->runPrequalifySeeders();

    $provinces = Province::select('id', 'name', 'majority_age')->get();
    $provinces->each(function ($province) {

        // Act
        $provinceName = str_replace(' ', '', strtolower($province->name));
        $username = "{$provinceName}@example.com";

        $applicant = $this->createApplicant($username);
        $this->login($applicant->username);

        Log::info($applicant->username); // correct username: check
        Log::info($this->token); // different token each time: check

        // Applicants can only have one application at a time hence the
        // new user for each iteration per province: fails on second
        // iteration as service container persisted... I think
        $application = $this->createNewApplication()->decodeResponseJson();

        // Removed for brevity...

        $this->logout();
    });
}

UPDATE

In case it was related to the use of each I pasted the statements a couple times and shifted the province off the collection, and it still thinks in Act 2 that the user of Act 1 is authenticated.

// Act 1
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}@example.com";

$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();

// Removed for brevity

$this->logout();

// Act 2
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}@example.com";

$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();

// Removed for brevity

$this->logout();

UPDATE 2

Even though I've been logging out methods on the server that are being invoked as endpoints are hit like login, logout, etc. I wanted to check of logout was actually working and blacklisting the token, and it is based on this test that throws an Unauthenticated 401 error after the second create new application endpoint is hit. So it really does seem like the service provider isn't being cleared.

// Act
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}@example.com";

$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();

$this->logout();

// 401 server response since token was successfully blacklisted
$application = $this->createNewApplication()->decodeJson();

Solution

  • So this appears to be due to the "subtlety of the framework" where the application instance is reused for all requests according to this Github issue.

    Apparently you should be able to clear out the IoC by invoking $this->refreshApplication(), but when I do this it drops the database since I'm using an in memory database.

    So instead I just delete the application on each iteration after assertions have been performed, which solves this particular issue.