Jyostna Ummadisetty
Jyostna Ummadisetty

Reputation: 41

Laravel - Unit test - variable of integer data type is coming as string while unit testing and the unit test is failing

I'm new to Laravel and PHP. I have created a migration 'Car' table with columns id, Make, Model, Year.I have used the seeder to make 50 cars using faker. I have written a unit test to test the data type of the year property to be int, but my unit test is failing. Could anyone pls help me on this?

Migration table:
 public function up()
    {
        Schema::create('cars', function (Blueprint $table) {
            $table->increments('id');
            $table->string('make');
            $table->string('model');
            $table-> integer('year');
            $table->timestamps();
        });

Factory:
$factory->define(App\Car::class, function (Faker $faker) {
    return [
        'make' => $faker->randomElement($array = array ('Ford','Honda','Toyota')),
        'model' => $faker->name,
        'year'   => $faker->year($max = 'now'),

Seeder
    public function run()
    {
        factory(App\Car::class, 50)->create()->each(function ($car) 
}

Unit test 
 public function testCarYearDataType()
    {
        $car = Car::find(1);
        dd($car->year);
      dd(gettype($car->Year));
       this->assertInternalType('int',$car->Year);
    }

Upvotes: 3

Views: 2748

Answers (4)

Illya Moskvin
Illya Moskvin

Reputation: 352

I'm guessing that you are using an in-memory SQLite database for testing:

<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>

If so, then you are running into this issue:

https://github.com/laravel/framework/issues/3548

It was closed in 2014, but never actually fixed. You have two options:

  1. Switch from testing in SQLite to testing in the same database engine as you use in production.
  2. Explicitly typecast all keys in your models.

For the second option, your Car model should have a $casts attribute that casts id to integer:

protected $casts = [
    'id' => 'integer',
];

The trade-off of having to explicitly cast keys might be worth keeping the speed that SQLite provides.

Upvotes: 3

usrNotFound
usrNotFound

Reputation: 2830

Not sure datatype is the right kind of test but anyway

The key thing to remember is you need to have a different database for testing my case I use memory so my phpunit.xml looks like the following

<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

The you will have your test class. In your case I would call it CarTest.php

<?php 

namespace Tests\Unit;

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;


class CarTest extends TestCase {

    use DatabaseTransactions;
    
   
    
    public function testCarYearDataType()
    {
    
     //just create 1 car as all the cars will have same data type anyway
      factory(App\Car::class)->create(); 

       this->assertInternalType('int', gettype(Car::first()->year));
    }
}

Upvotes: 1

patricus
patricus

Reputation: 62398

It may be a typo in your question, but fields are case sensitive. $car->year will give you the value in your year field. $car->Year will give you null, since you don't have a Year field.

As for the type of the field returned from the database, that can differ depending on the underlying database and the drivers used to access that database. If you want to avoid all uncertainty, you need to add your field to your $casts attribute on your model:

protected $casts = [
    'year' => 'int',
];

Now the field will always be cast to a PHP integer when accessed.

Upvotes: 0

jeremykenedy
jeremykenedy

Reputation: 4285

Try updating your model to prevent these collisions with typecasting:

/**
 * The attributes that are not mass assignable.
 *
 * @var array
 */
protected $guarded = [
    'id',
];

/**
 * Typecasting is awesome and it prevents errors.
 *
 * @var array
 */
protected $casts = [
    'make'     => 'string',
    'model'    => 'string',
    'year'     => 'integer',
];

/**
 * The attributes that should be mutated to dates.
 *
 * @var array
 */
protected $dates = [
    'created_at',
    'updated_at',
];

Upvotes: 1

Related Questions