skip to Main Content

In a Laravel project I have to store some data in json. For phpUnit tests I use a factory with faker. I try to fake a json structure for the tests, but it always fail on validation. Is there any proper way to create a json in factory that passes the validation for json?

I tried a simple json array, and the json array with json_encode, both of them failed at validation, and gives errors.

With simple json like: 'settings' => ['areas' => ['full', 'city']]
the error is:

Property [settings] is not of expected type [json].
Failed asserting that an array contains 'array'.

With json_encode like: 'settings' => json_encode(['areas' => ['full', 'city']])
the error is:

Property [settings] is not of expected type [json].
Failed asserting that an array contains 'string'.

My model:

class Example extends Model
{
    protected $fillable = [
        'name',
        'settings'
    ];

    public static $rules = [
        'name' => 'required|string|max:255',
        'settings' => 'nullable|json'
    ];

    protected $casts = 
        'settings' => 'array'
    ];

}

My factory:

<?php
class ExampleFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Example::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->words(3, 7),
            'settings' => json_encode(['areas' => ['full', 'city']])  // or what?
        ];
    }
}

In my test file:


    /** @test */
    public function shouldStore(): void
    {
        $item = $this->model::factory()->make();
        $data = $item->toArray();
        $this->post(action([$this->controller, 'store']), $data)
            ->assertOk();
    }

3

Answers


  1. You need to cast the property to array:

    SomeModel extends Model
    {
        protected $casts = [
            'settings' => 'array',
        ];
    }
    
    Login or Signup to reply.
  2. Your issue is that you are casting the property to array, but you are storing a string, you should be passing an array.

    Do this:

    class ExampleFactory extends Factory
    {
        /**
         * The name of the factory's corresponding model.
         *
         * @var string
         */
        protected $model = Example::class;
    
        /**
         * Define the model's default state.
         *
         * @return array
         */
        public function definition()
        {
            return [
                'name' => $this->faker->words(3, 7),
                'settings' => ['areas' => ['full', 'city']],
            ];
        }
    }
    

    And the rule (no idea where are you using that) should be like this:

    'settings' => 'nullable|array'
    
    Login or Signup to reply.
  3. Your model works just fine with an array because the cast takes care of the conversion for you. However when posting to the controller you need to manually cast the data to JSON:

    public function shouldStore(): void
    {
        $item = $this->model::factory()->make();
        $data = $item->toArray();
        $data['settings'] = json_encode($data['settings']);
        $this->post(action([$this->controller, 'store']), $data)
            ->assertOk();
    }
    

    However this does mean that you may need to json_decode the data in the controller before creating the model.

    Alternatevely you can do what @matiaslauriti is suggesting and post the data as an array to begin with

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search