channaveer's avatar

Help needed to refactor the working test case

Hi, I am very new to Laravel Testing. I want to make sure if I am on the right track in creating test cases.

In the following account_verification test since I am verifying the account should I be least bothered about Registering User, Creating Token, and concentrate more on Email Verification request? If yes then how?

**NOTE: ** I don't have any knowledge of TESTDOUBLES (Mock, Spy, Dummy, Fake, Stub).

If the same can be achieved with TESTDOUBLES can you please guide me on the same? Also, where can I learn more about the TESTDOUBLES and when to use which one? Any resource links are much appreciated.

/**
* @test
*/
public function account_verification()
{
    $user = [
        "name" => "John Doe",
        "email" => "[email protected]",
        "password" => "test@123",
        "confirm_password" => "test@123"
    ];

    /** Register user where email_verified_at column will be null */
    $this->postJson("/api/auth/register", $user)
        ->assertStatus(Response::HTTP_OK);

    /** Get the registered user token */
    $passwordReset = PasswordReset::where(["email" => $user["email"]])->first();

    /** Verify user account by sending proper TOKEN & EMAIL */
    $this->getJson("/api/auth/verify-email?token={$passwordReset["token"]}&email={$user["email"]}")
        ->assertStatus(Response::HTTP_OK)
        ->assertJson([
            "message" => "User account verified successfully"
        ]);

    /** Assert that user email_verified_at column is not null */
    $user = User::where(["email" => $user["email"]])->first();
    $this->assertNotNull($user->email_verified_at);

    /** Make sure that the tokens are deleted after account verification */
    $this->assertDatabaseMissing("password_resets", [
        "token" => $passwordReset->token
    ]);
}
0 likes
4 replies
goldeneye's avatar

You should separate that 2 http endpoints to 2 different tests. First test should test register endpoint and 2nd test should test /verify-email endpoint. The best place to learn is Laravel testing documentation https://laravel.com/docs/8.x/testing

Things you should consider to use:

Factories: https://laravel.com/docs/8.x/database-testing#defining-model-factories

$user = User::factory()->create([
        "name" => "John Doe",
        "email" => "[email protected]",
        "password" => "test@123",
        "confirm_password" => "test@123"
]);

Fakes: https://laravel.com/docs/8.x/mocking#mail-fake (you can use Listeners/Events fakes as well)

Mail::assertSent(VerifyEmail::class, function ($mail) use ($user) {
    return $mail->hasTo($user->email) &&
           $mail->hasCc('...') &&
           $mail->hasBcc('...');
});

Database assertions: https://laravel.com/docs/8.x/database-testing#available-assertions

$this->assertDatabaseHas('users', [
    'email' => '[email protected]',
]);

for register endpoint you can check email and what is in database after your test

forverify-email you can use factories to populate data and use db assertions to check if it is as you expect after calling the endpoint.

your test should also cover scenarios when assertStatus will not be Response::HTTP_OK

channaveer's avatar

@goldeneye Thank you very much for reverting back.

I have already written test cases for User Registration in RegisterTest.php files. I am already handling mocks of Events & Jobs with the help of Laravel Mock stuff.

I wanted to know how can I remove the dependency of the test case and approach it directly.

By dependency what I meant was the token will get created after user registration and then he can verify his email. So to test verify email I have to even make a user registration API call.

channaveer's avatar

Will try an alternative way to approach with PasswordResetFactory and let me see if I can achieve it.

channaveer's avatar
channaveer
OP
Best Answer
Level 1

Finally, I was able to refactor to the following

/**
* @test
*/
public function account_verification()
{
    $passwordReset = PasswordReset::factory()->create();

    /** Verify user account by sending proper TOKEN & EMAIL */
    $this->getJson("/api/auth/verify-email?token={$passwordReset->token}&email={$passwordReset->email}")
        ->assertStatus(Response::HTTP_OK)
        ->assertJson([
            "message" => "User account verified successfully"
        ]);

    /** Assert that user email_verified_at column is not null */
    $user = User::where(["email" => $passwordReset->email])->first();
    $this->assertNotNull($user->email_verified_at);

    /** Make sure that the token get deleted after account verification */
    $this->assertDatabaseMissing("password_resets", [
        "token" => $passwordReset->token
    ]);
}

PasswordResetFactory.php

public function definition()
{
    return [
        "email" => function () {
            return User::factory()->create()->email;
        },
        "token" => Str::uuid()
    ];
}

Just in case if anyone can improve my solution then I would be glad.

Please or to participate in this conversation.