-
Notifications
You must be signed in to change notification settings - Fork 11.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[11.x] Constrain key when asserting database has a model #52464
[11.x] Constrain key when asserting database has a model #52464
Conversation
I like the idea, but this is breaking and shouldn't be targetted at 11.x. Changing the signature of I also don't think it makes sense to make |
The breaking changes are not in the application layer and would only affect those that extend This change will also uncover any false positives that may exist in a test suite due to those reasons. This could easily target the next major release, however it needs to be weighed up on the additional work load around that release (upgrade docs, conflicts, five months of rebasing) versus how many tests this will affect. Remembering that it's a test only change, no application will break from this change. It's not uncommon for a minor release to break tests, here are some examples I can think of:
Anyone using a new model instance can replace it with the class string using the following regex.
Regarding, $user = new User(['exists' => true]);
$this->assertModelExists($user); // am I using this correctly?
$this->assertDatabaseHas($user); // fail, much clearer A common pattern I see in feature tests is making a model using a factory, sending the request then refreshing the instance and asserting it's attributes, this cleans that up. // arrange
$user = User::factory()->create(['name' => 'Pat', 'role' => 'developer']);
// act
$this->patchJson(route('users.update', $user), ['name' => 'Cinderella'])
->assertOk();
// refresh and assert
$user->refresh();
$this->assertSame('Cinderella', $user->name);
$this->assertSame('developer', $user->role);
// or just assert the database has it
$this->assertDatabaseHas($user, ['name' => 'Cinderella', 'role' => 'developer']); |
People can also use |
* @return void | ||
*/ | ||
public function __construct(Connection $database, array $data) | ||
public function __construct(Connection $database, array $data, $model) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of changing the signature here, would it be better to do something like this in the assert
methods?
if ($connection instanceof Model && $connection->getKey()) {
$data = array_merge($data, [$connection->getKeyName() => $connection->getKey()]);
}
So that it's just injecting the model's key into the data that it's checking for if it isn't already set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's definitely possible, but would result in duplicate code in both assertDatabaseHas()
and assertDatabaseMissing()
methods, as all roads lead to HasInDatabase
I moved it there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't really love injecting $model
here. Doesn't feel like this class should have anything to do with Eloquent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let me refactor this a little.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have left HasInDatabase
as is. This should be a little more palatable. Thanks for the push in the right direction @samlev
49ddaa2
to
424df65
Compare
Thanks 👍 |
There are currently three options to pass as the first argument to
assertDatabaseHas()
andassertDatabaseMissing()
This PR adds a constraint on the model's key when an eloquent model is used so the last assertion above would fail.
Breaking Change
Like the above example, if a test suite currently asserts a database row contains data against a different model instance that test will now fail. However, I would argue that the test gives a false positive and should change to use the class string or the correct model instance.
Additionally, passing an empty model instance is an anti-pattern as until it is saved, the database does not have that model.
Currently the following test would pass:
This change will only break those specific tests not the application logic.