Reputation: 478
Laravel 7. I user http tests to check the availability status of my pages. But I noticed that in tests one of my policy works differently.
The controller method:
public function show(Competition $competition)
{
$competition->load('registeredTeams.user', 'teams.user');
return view('competitions.show', compact('competition'));
}
The policy method applied to it:
public function view(User $user, Competition $competition)
{
return $user->id === $competition->user_id;
}
The test and its internal methods:
public function testCompetitionsPageTest()
{
$user = $this->loginAsFakeUser();
$user->commissioner = true;
$user->save();
$competition = $this->createFakeCompetition($user->id);
$this->get('/competitions')->assertOk();
$this->get('/competitions/create')->assertOk();
$this->get('/competitions/' . $competition->id)->assertOk();
}
protected function loginAsFakeUser()
{
$user = User::create(['username' => 'TestUserForTests']);
$this->actingAs($user);
return $user;
}
protected function createFakeCompetition(int $user_id)
{
$competition = new Competition([
'name' => 'TestCompetitionName',
'type' => competitionTypes()[2],
'tops_number' => 1,
'self_confirm' => 1,
'winner_points' => 3,
'registration_end' => today()->toDateString(),
]);
$competition->user_id = $user_id;
$competition->save();
return $competition;
}
When I load the tested pages in a browser, everything works just fine. However, when I test it, the next assertion doesn't work as it returns 403 code instead of 200:
$this->get('/competitions/' . $competition->id)->assertOk();
After some manipulations I figured out that it works correctly if I use non-strict comparison in my policy:
return $user->id === $competition->user_id;
Can you help me to figure out why my created user through create()
and actingAs($user)
id and user_id field of $competition have different types?
Edited (added requested information): My competition migration:
Schema::create('competitions', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('info', 1000)->nullable();
$table->string('logo')->nullable();
$table->foreignId('user_id')->constrained();
$table->string('type');
$table->json('parameters')->nullable();
$table->unsignedTinyInteger('self_confirm');
$table->unsignedTinyInteger('tops_number');
$table->unsignedTinyInteger('winner_points');
$table->unsignedSmallInteger('round')->default(0);
$table->date('registration_end');
$table->unsignedSmallInteger('max_teams')->default(0);
$table->date('finished')->nullable();
$table->timestamps();
});
Competition model (it contains a lot of methods to request and calculate additional data only, I included only properties):
protected $casts = [
'parameters' => 'object',
];
protected $fillable = [
'name',
'info',
'registration_end',
'self_confirm',
'winner_points',
'tops_number',
'type',
];
I tested the values on different stages and it turned out that for some reason during the test an injected Model Competition has user_id property with the type of string for some reason. In a browser it is an integer value as expected.
Upvotes: 0
Views: 704
Reputation: 478
I found the problem. It was because of my test database type. It used sqlite, which has only string values in it after retrieving results. I just removed two lines from my phpunit.xml in order to use my main connection for tests as well.
Removed lines:
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
Upvotes: 0
Reputation: 51
Just making some assumptions on the code that you posted I think that, outside the test, $competition->user_id
is a string
value while inside the test it's an int
value. You can easily verify this assumption.
This happens because createFakeCompetition
forces the type int
for $user_id
, as you are running without strict mode, PHP converts the string passed to an inteteger, however this makes the comparison $user->id === $competition->user_id
fail.
I hope to have been clear.
Upvotes: 1