After upgrading from 6.x to 7.x I found existing assertDatabaseHas() tests are failing, due to the date-serialization change https://laravel.com/docs/7.x/upgrade#date-serialization.
I would expect these tests to still pass, is my expectation correct?
A simple example is to save a model to the database then assert the database has it exists. eg. for a post model;
$this->assertDatabaseHas('posts' , $post->toArray());
The new date serialisation changes don't affect how dates are stored in the database. It only affects how they are serialised when casting to an array or JSON string using the toArray() and functions alike.
if this is the case what are peoples recomendations on the best approach to test that all attributes of a model are correctly saved on the database?
on laravel 6 I have usually called toArray() eg $this->assertDatabaseHas('posts' , $post->toArray());
The above will fail since calling toArray() will serialise the date differently to the database. I would manually create the array, formatting the dates yourself. This is also safer.
Please take the approach @ryangjchandler suggested. There's nothing wrong with the assertDatabaseHas method.
Hi @driesvints I seem to be having the above issue as well but without the array serialisation. I've replicated it in a very simple test:
public function testBasicTest()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->date('date_of_birth');
});
User::forceCreate(['date_of_birth' => Carbon::parse('2020-04-15')]);
$this->assertDatabaseHas('users', ['date_of_birth' => '2020-04-15']);
}
vagrant@homestead:~/Code/tmp$ vendor/bin/phpunit
PHPUnit 8.5.3 by Sebastian Bergmann and contributors.
.F 2 / 2 (100%)
Time: 2.06 seconds, Memory: 24.00 MB
There was 1 failure:
1) Tests\Feature\ExampleTest::testBasicTest
Failed asserting that a row in the table [users] matches the attributes {
"date_of_birth": "2020-04-15"
}.
Found: [
{
"id": "1",
"date_of_birth": "2020-04-15 00:00:00"
}
].
/home/vagrant/Code/tmp/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:25
/home/vagrant/Code/tmp/tests/Feature/ExampleTest.php:29
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
The user model has the cast type set to date.
@jamiehd try ['date_of_birth' => Carbon::parse('2020-04-15')->toDateTimeString()]
@paul-a-byford
Ran into this issue too, just been digging through the framework and getAttributes() seems to do the trick in replicating the old toArray() in database tests. Just replaced on my tests and all passed.
Few more keystrokes but easier than adding serializeDate to all your models or generating arrays for all DB tests
I don't tend to you use casts much, so not sure of any affects when using casts with this method.
@paul-a-byford
Ran into this issue too, just been digging through the framework and getAttributes() seems to do the trick in replicating the old toArray() in database tests. Just replaced on my tests and all passed.
Few more keystrokes but easier than adding serializeDate to all your models or generating arrays for all DB tests
I don't tend to you use casts much, so not sure of any affects when using casts with this method.
@colinhall17 legend! that is ideal for my use case and as lot more straightforward than manually generating arrays. Thanks for the advice
I just found another option in the Laravel Documentation. You can add the following code to your model and it will allow you to use the toArray() method in your tests.
/**
* Prepare a date for array / JSON serialization.
*
* @param \DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
Hi @jringeisen
Yes you can do that on all your models, or extend another base model, but the reason it was changed in Laravel was to bring API dates into an ISO-8601 standard, which is what we are told we should be doing to make our API's standard.
APi's utilise the toArray function, which will then serialize the date into the correct ISO standard format for when its returned.
If we override on the model we are losing the ISO standard..
So the best for me is to use getAttributes in tests and keep it so we can have correctly formatted ISO API dates.
Most helpful comment
@paul-a-byford
Ran into this issue too, just been digging through the framework and getAttributes() seems to do the trick in replicating the old toArray() in database tests. Just replaced on my tests and all passed.
Few more keystrokes but easier than adding serializeDate to all your models or generating arrays for all DB tests
I don't tend to you use casts much, so not sure of any affects when using casts with this method.