Summer Sale! All accounts are 50% off this week.

laracastsluvr's avatar

Laravel phpunit testing actions requiring password confirmation

Hello,

As the title suggests, I'm trying to cover with tests some controller actions that require password confirmation before continuing with the intended "action" of the authenticated user.

Google search get's confused with my query and returns only how to implement password confirmation or password validation. This is done and works just fine.

// In web.php routes
Route::post('deactivate-account', controller-stuff)->middleware(['password.confirm'])->name('deactivation-route');
// in a test class
...
$this
    ->actingAs($user)
    ->from('/anywhere')
    ->post(route('deactivation-route'))
    ->assertRedirect(route('password.confirm')); // This is ok, works
...

That's for a test case for example: "user_gets_password_confirmation_prompt_when_deactivating_account()"

But can't figure out how to test the entire flow without ending up "disabling" middleware handling with some dark magic code. It doesn't feel right.

Is there any way to make the test pass the entire flow, and have everything working without disabling anything?? I hope I missed something and it is "as simple as that" :)

Thanks in advance!

0 likes
10 replies
automica's avatar

I’m assuming you are firing your password confirmation prompt using js? If so you won’t be able use php unit to test this as it run js files attached to your html,

You should look at using laravel dusk or selenium for this, or create a front end test using something like cypress.

laracastsluvr's avatar

@automica I don't touch front-end stuff. I write the test cases, build the backend code that makes the test pass and don't worry at all about the front end stuff.

tykus's avatar

@laracastsluvr you can use Browserkit for testing interactions such as you describe. It allows you to interact with forms and make assertions about responses/views

laracastsluvr's avatar

@tykus Sure, but I don't do any work on front end stuff. I interact only with back end stuff.

laracastsluvr's avatar

@tykus Of course, but from their README, it clearly requires Views to exist, with HTML and Forms ;) I don't have access to any of that. Nothing of that sorts is built yet. Only Routes and Controllers+models. I create the "endpoints" for front-end people to send their front-end interactions to

tykus's avatar

@laracastsluvr in that case, this...

->assertRedirect(route('password.confirm'));

...is the end of your test's scope.

laracastsluvr's avatar

@tykus I did some illogical test code out of frustration and it passes, but I don't know if it is just a "false positive". It probably is, because I don't understand why it passes

...
$response = $this
    ->actingAs($user)
    ->from('/anywhere')
    ->post(route('user.settings.deactivate'))
    ->assertRedirect(route('password.confirm'));

$response = $this
    ->actingAs($user)
    ->post($response->baseResponse->getTargetUrl(), ['password' => 'superduperpassword']);

$response = $this
    ->actingAs($user)
    ->post(route('user.settings.deactivate'))
    ->assertRedirect('/login');

$this->assertGuest();
$this->assertNotNull($user->deactivated_at);
$this->assertTrue($user->deactivation_reason->is(UserDeactivationReason::SELF));
...

In reality I was expecting to get just redirected to the password confirmation route again... but it didn't. Its 23:15.. too late to understand :)

laracastsluvr's avatar

Seems like everything runs within the same session and doing those 3 post requests works. If I comment out the password post, the 3rd post acts like the first and redirects again to the password confirmation route. Hmm...

MobyDuck's avatar

I had the same issue - with Fortify, some routes require password confirmation and it's nice to be able to test all that. This worked for me:

$user = User::factory()->create();

// Try to go to the protected route
$response = $this->actingAs($user)->get('/user/delete');

// Get prompted for Fortify password confirmation
$response->assertRedirect('/user/confirm-password');

// Confirm the password
$response = $this->actingAs($user)->post('/user/confirm-password', [
    'password' => 'password',
]);

// Get redirected to the protected route
$this->followRedirects($response)->assertSee('Delete your account?');

Please or to participate in this conversation.