Search code examples
laraveltestingphpunitlaravel-6laravel-factory

Factory counts 2 models instead of 1 in tests


I have these migrations:

class CreateOrdersTable extends Migration
{
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users');
        });
    }
}
class CreatePaymentsTable extends Migration
{
    public function up()
    {
        Schema::create('payments', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('order_id');
            $table->timestamps();

            $table->foreign('order_id')->references('id')->on('orders');
        });
    }
}

and these factories:

$factory->define(Payment::class, function (Faker $faker) {
    return [
        'order_id' => factory(Order::class)->create(),
    ];
});
$factory->define(Order::class, function (Faker $faker) {
    return [
        'user_id' => factory(User::class)->create(),
    ];
});

Now in my test i have this:

/** @test */
public function it_should_count_1_order()
{
     $order = factory(Order::class)->create();

     $payment = factory(Payment::class)->create([
         'order_id' => $order->id,
     ]);

     $this->assertEquals(1, Order::count())
}

The Order table count gives me 2. why? it should be 1 since I am telling payment factory to override order_id with the given order. Am I missing something?


Solution

  • You should try defining your factory like this:

    $factory->define(Order::class, function (Faker $faker) {
        return [
            'user_id' => factory(User::class),
    });
    

    If it does not help, make sure you use in your tests DatabaseTransactions or DatabseMigrations test and you clear database before running test 1st time. Otherwise each time you run test you will be getting more and more records in database.

    Personally, I think using assertion like this:

    $this->assertEquals(1, Order::count())
    

    in tests is not a good idea. You might decide to create some other additional data when you create more advanced test and you should not care about models created before. I personally would like this test like this:

    $initialOrdersCount = Order::count();
    
    // here you run some actions you want to test
    
    $this->assertSame($initialOrdersCount  + 1, Order::count());
    

    This way, in case anything else happens in application or will be added to test, I don't care if I have in database 2 or 3 models. For me it's important that number or orders should be increased by one.