In my Laravel application I am trying to get into feature testing and have started with a model called Announcement.
One test I'm running is whether a user can create an instance of Announcement and persist it to the database.
My test is as follows:
/** @test */
public function a_user_can_create_an_announcement()
{
$this->withoutExceptionHandling();
$this->setupPermissions();
$announcement = factory(Announcement::class)->raw();
$this->actingAs(factory(User::class)->create())->get(route('announcements.index'))->assertStatus(200);
$this->post(route('announcements.store', $announcement))->assertStatus(302);
$this->assertDatabaseHas('announcements', $announcement);
}
Now as far as I understand factory(Announcement::class)->raw(); returns a new Announcement as an array using the relevant model factory.
I then make a request to my store endpoint with the data array and expect to redirected so I add the following:
$this->post(route('announcements.store', $announcement))->assertStatus(302);
The final line is to check the announcement was written to the database table called announcements
I get the following error from the test case:
1) Tests\Feature\AnnouncementsTest::a_user_can_create_an_announcement
Failed asserting that a row in the table [announcements] matches the attributes {
"message": "King. 'When did you.",
"message_details": "The Caterpillar.",
"author": "beatrice-herzog",
"status": "pending",
"published_at": null,
"created_at": {
"date": "2019-05-16 04:13:12.000000",
"timezone_type": 3,
"timezone": "Europe\/London"
},
"updated_at": "2019-08-20T13:37:22.293428Z"
}.
Found: [
{
"id": 5,
"message": "King. 'When did you.",
"message_details": "<p>The Caterpillar.<\/p>",
"author": "hollis-dach",
"status": "pending",
"published_at": null,
"created_at": "2019-08-20 14:37:23",
"updated_at": "2019-08-20 14:37:23"
}
].
Here is my AnnouncementFactory
<?php
/* @var $factory \Illuminate\Database\Eloquent\Factory */
use Faker\Generator as Faker;
use App\User;
use App\Announcement;
use Carbon\Carbon;
$factory->define(Announcement::class, function (Faker $faker) {
$user = factory(User::class)->create();
return [
'message' => $faker->realText(25),
'message_details' => $faker->realText(20),
'author' => $user->username,
'status' => 'pending',
'published_at' => null,
'created_at' => $faker->dateTimeThisYear('now', 'Europe/London'),
'updated_at' => Carbon::now()
];
});
Here is my AnnouncementModel
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Purifier;
use Carbon\Carbon;
use App\Like;
class Announcement extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'message', 'message_details', 'status', 'published_at'
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'published_at',
'created_at',
'updated_at',
];
/**
* Get the user that posted this announcement
*/
public function user()
{
return $this->belongsTo(User::class, 'author', 'username');
}
/**
* Get the users that have liked this article
*
* @return void
*/
public function likes()
{
return $this->morphToMany(User::class, 'likeable');
}
/**
* Purify the content of message details when it is set so that it isn't vulnerable to XXS attacks
*
* @param string $value
* @return void
*/
public function setMessageDetailsAttribute($value)
{
$this->attributes['message_details'] = Purifier::clean($value);
}
/**
* Generate a nicer format for created_at
*
* @return void
*/
public function getCreatedAtAttribute($value)
{
return Carbon::parse($value)->format('d F Y');
}
/**
* Determine whether an announcement is pending
*
* @return void
*/
public function getPendingAttribute()
{
return $this->status == 'pending' ? true : false;
}
/**
* Check if the user has liked this announcement
*
* @return void
*/
public function getUserHasLikedAttribute()
{
$like = $this->likes()->whereUserId(auth()->user()->id)->first();
return (!is_null($like)) ? true : false;
}
/**
* Get the users that have liked this article
*
* @return void
*/
public function getLikesCountAttribute()
{
return $this->likes()->count();
}
/**
* Get count of users who liked the announcement excluding the logged in user
*
* @return void
*/
public function getLikesCountExcludingAuthUserAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->count();
}
/**
* Get random user who liked this announcement
*
* @return void
*/
public function getRandomUserWhoLikedThisAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->inRandomOrder()->first();
}
/**
* Get all users who liked this announcement
*
* @return void
*/
public function getUsersWhoLikedThisAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->get();
}
/**
* Scope an article by whether or not it's published
*/
public function scopePublished($query)
{
return $query->where('status', 'published');
}
/**
* Scope an article by whether or not it's drafted
*/
public function scopePending($query)
{
return $query->where('status', 'pending');
}
/**
* Scope an article by whether or not it's archived
*/
public function scopeArchived($query)
{
return $query->where('status', 'archived');
}
}
Do the attributes literally have to be identical or am I just using tests incorrectly?
Do factories use model accessors and mutators?