Search code examples
phplaraveltestinglaravel-factory

How to use $dates array and Factory together


I have a factory with the below definition

public function definition()
{
    $startDate = Carbon::createFromFormat('Y', $this->faker->year);
    $endDate = $startDate->addYear();

    return [
        'name' => $startDate->year,
        'start_date' => $startDate->format('Y-m-d'),
        'end_date' => $endDate->format('Y-m-d'),
    ];
}

So this generates an array like below

[
    'name' => 2020,
    'start_date' => '2020-01-30',
    'end_date' => '2021-01-30'
]

The above worked as expected until a requirement to return the start_date field and the end_date field arose. So in my model I added:

protected $dates = ['created_at', 'updated_at', 'start_date', 'end_date'];

In my validators, I validate that the format is Y-m-d.

public function rules()
{
    return [
        'name' => 'required|unique:academic_years',
        'start_date' => 'required|date_format:Y-m-d|before_or_equal:end_date',
        'end_date' => 'required|date_format:Y-m-d',
    ];
}

I also have the below test:

public function should_throw_error_if_date_format_is_invalid()
{
    Permission::factory()->state(['name' => 'create academic year'])->create();

    $user = User::factory()->create();
    $user->givePermissionTo('create academic year');

    $this->actingAs($user, 'api')->postJson('/api/academic-years', [
        'name' => $this->faker->year,
        'start_date' => '01-01-2019',
        'end_date' => '2019-01-01'
    ])->assertStatus(422);

    $this->actingAs($user, 'api')->postJson('/api/academic-years', [
        'name' => $this->faker->year,
        'end_date' => '01-01-2019',
        'start_date' => '2019-01-01'
    ])->assertStatus(422);
}

The test fails, but If I remove the $dates array, the test passes. On checking the object passed into the request I see a different date format {"name":1995,"start_date":"1995-04-04T00:00:00.000000Z","end_date":"1995-04-04T00:00:00.000000Z"} from the format provided in the Factory.

How can I resolve this?


Solution

  • Since Faker is returning a DateTime-Object which isn't representing a date-string like mysql likes it (Y-m-d H:i:s).

    You should however be able to access the objects property date to get the correct string like this:

    Try this

    public function definition()
    {
        $startDate = Carbon::createFromFormat('Y', $this->faker->year);
        $endDate = $startDate->addYear();
        
        return [
            'name' => $startDate->year,
            'start_date' => $startDate->format('Y-m-d H:i:s'),
            'end_date' => $endDate->format('Y-m-d H:i:s'),
        ];
    }
    

    This should return a date like this string '1995-04-04T00:00:00.000000Z'

    or

    By default, timestamps are formatted as 'Y-m-d H:i:s'. If you need to customize the timestamp format, set the $dateFormat property on your model. This property determines how date attributes are stored in the database, as well as their format when the model is serialized to an array or JSON:

    <?php
    
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    
    class Flight extends Model
    {
        /**
         * The storage format of the model's date columns.
         *
         * @var string
         */
        protected $dateFormat = 'U';
    }