laracasts / TestDummy

Easy factories for PHP integration testing.
https://laracasts.com/lessons/whats-new-in-testdummy
MIT License
455 stars 80 forks source link

Issue with using strings as primary id #102

Closed chrissm79 closed 9 years ago

chrissm79 commented 9 years ago

I'm having a really strange issue when attempting to use strings as primary ids. Running both of the tests below, the RegistrationTest (which runs second) fails and I get:

QueryException in Connection.php line 624:
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: users.id (SQL: insert into "users" ("first_name", "last_name", "email", "type", "password", "updated_at", "created_at") values (Friedrich, Gutkowski, john.doe@example.com, patient, y$i20McfZTbl3K/MhCU/8p8.buhNe0eT9NNKMjL2nowCCbyYtpnvbIu, 2015-05-19 20:18:45, 2015-05-19 20:18:45))

What's so strange is if I run the tests separately, they work just fine. I created my own custom builder class and inside the "save" method I set the id of the model (return (string) new MongoId()), but it is still failing. If I remove the MongoIdTrait and use increments in my migrations, both tests run fine.

<?php

use Laracasts\TestDummy\Factory as TestDummy;

class AuthTest extends TestCase {

    /** @test */
    public function it_authenticates_user_and_returns_token()
    {
        $user = TestDummy::create('App\Models\User', [
            'password' => Hash::make('password')
        ]);

        $this->post('/auth', [
            'email'    => $user->email,
            'password' => 'password'
            ])->seeStatusCode(200)
            ->seeJson();
    }
}
<?php

use Laracasts\TestDummy\Factory as TestDummy;

class RegistrationTest extends TestCase {

    /** @test */
    public function it_registers_account()
    {
        $user = TestDummy::attributesFor('App\Models\User', [
            'email'     => 'john.doe@example.com',
            'activated' => null
        ]);

        $credentials = array_merge($user, [
            'password' => 'password',
            'password_confirmation' => 'password'
        ]);

        $this->post('/register', $credentials)
            ->seeIsJson()
            ->seeJsonContains(['email' => 'john.doe@example.com'])
            ->seeInDatabase('users', ['email' => 'john.doe@example.com'])
            ->seeStatusCode(200);
    }
}
<?php

// tests/factories/factory.php

$factory('PACT\Models\User', [
    'first_name' => $faker->firstName,
    'last_name'  => $faker->lastName,
    'email'      => $faker->email,
    'password'   => bcrypt('password'),
    'activated'  => true,
    'type'       => 'patient'
]);

My App\Models\User extends uses the following trait:

<?php namespace App\Traits;

trait MongoIdTrait {

    /**
     * Attach MongoId to id attribute
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function($model)
        {
            $key = $model->getKeyName();

            if(empty($model->{$key}))
            {
                $model->{$key} = (string) $model->generateNewId();
            }
        });
    }
    /**
     * Generate new MongoId
     *
     * @return \MongoId
     */
    protected function generateNewId()
    {
        return new \MongoId();
    }

}
<?php

use Laracasts\Integrated\Extensions\Laravel as IntegrationTest;
use Laracasts\Integrated\Services\Laravel\DatabaseTransactions;
use Laracasts\TestDummy\Factory;

class TestCase extends IntegrationTest {

    use DatabaseTransactions;

    public function createApplication()
    {
        ...
    }

    public function setUp()
    {
        parent::setUp();

        Factory::$databaseProvider = new \MongoFactory;

        Artisan::call('migrate');
    }
}
<?php

use Laracasts\TestDummy\EloquentModel;

class MongoFactory extends EloquentModel {

    /**
     * Persist the entity.
     *
     * @param  Model $entity
     * @return void
     */
    public function save($entity)
    {
        $entity->id = (string) new \MongoId();

        parent::save($entity);
    }
}
chrissm79 commented 9 years ago

This issue can be closed. It seems to be a problem with PHPUnit/Laravel. Adding an event listener on the model::creating event and attaching the id (rather than in the models' boot method) resolves the issue.