Search code examples
phpunit-testinglumen

PHP Unit testing setting JWT token after login test completes


Hi I'm trying to set JWT token after the login test is complete in a global var and then extend all tests off of that class, now I'm not sure if I am even doing it right so any help is appreciated or documentation I could look up

<?php
use Illuminate\Support\Facades\Config;

class AuthenticationTest extends TestCase {
    /**
     * @var string
     */
    public $token = '';

    /**
     * Register test user
     *
     * @return void
     */
    public function testRegister()
    {
        $this->json('post', '/auth/register', [
            'email' => Config::get('tests.email'),
            'password' => Config::get('tests.password'),
            'companyName' => Config::get('tests.companyName'),
            'firstname' => Config::get('tests.firstname'),
            'lastname' => Config::get('tests.lastname')
        ]);
        $this->assertEquals(200, $this->response->status());
        $this->seeJsonEquals([
            'message' => "Registration successful, please confirm email to login",
            'status'  => true,
        ]);
    }
    /**
     * Login test user
     *
     * @return void
     */
    public function testLogin()
    {
        $email = Config::get('tests.email');
        $password = Config::get('tests.password');
        $encryptedToken = base64_encode($email . ":" . $password);
        $this->json('post', 'auth/login', ['token' => $encryptedToken]);
        $this->assertEquals(200, $this->response->status());
        $this->seeJsonStructure([
            'token',
            'refresh',
            'status'
        ]);
        $content = json_decode($this->response->getContent());
        $this->assertObjectHasAttribute('token', $content);
        $this->token = $content->token;
        $this->token;

    }
}
?>

The login test works as it should but the token isn't being set for other tests that extend off of this test case ad use $this->token to send the JWT token


Solution

  • PHPUnit creates a new instance of AuthenticationTest for every test method to run each test in isolation.

    For tests that need an authenticated user, you have to obtain the token somehow. You could make an api request like you did in testLogin. A bit faster and less brittle way would be to create the token programatically using the same service as in your production code.

    To reduce boilerplate, create a test case for tests with an authenticated and create the token in the setup method. Tests that extend from this class have the token property. Alternatively, you could also make this functionality a trait.

    EDIT:

    Here's a simple code example.

    abstract class AuthenticatedUserTestCase extends TestCase
    {
        protected function token(): string
        {
            $email          = Config::get('tests.email');
            $password       = Config::get('tests.password');
            $encryptedToken = base64_encode($email . ':' . $password);
            $response       = $this->json('post', 'auth/login', ['token' => $encryptedToken]);
            $content        = json_decode($response->getContent());
    
            if (!isset($content->token)) {
                throw new RuntimeException('Token missing in response');
            }
    
            return $content->token;
        }
    }
    
    final class UserProfileTest extends AuthenticatedUserTestCase
    {
        public function test_that_user_can_access_their_profile_page()
        {
            $response = $this->json('get', '/user/profile', [], ['token' => $this->token()]);
            
            // ... assert stuff
        }
    }