Search code examples
phpdatabaselaravelcodeceptionlaravel-artisan

+Testing an artisan command that edits the database


I'm trying to test an artisan command that is designed to do some database maintenance.

In particular, it searches for records that don't have a column filled, and fill it.

This is a simplified version of the fire() method of the command:

public function fire()
{
    $items = Item::all();

    $total = $items->count();
    $updated = 0;

    foreach ($items as $item) {
        if ($item->myColumn != 'x') {
            $item->myColumn = 'x';
            $item->save();

            $updated++;
        }
    }

    $this->info("total: $total updated: $updated");
}

My (acceptance) test is very simple and does the following:

  • insert a record in the db
  • call the artisan command
  • check that the inserted record has been updated

This is the code:

public function doTheTest(AcceptanceTester $I)
{
    $I->wantTo('setup the myColumn when it is not set');

    $id = $I->haveRecord('items', [
        'myColumn' => '',
    ]);

    $I->runShellCommand('php artisan items:updater');

    $I->seeRecord('items', [
        'id'       => $id,
        'myColumn' => 'x',
    ]);
}

However the test fails, and I get the following message:

Couldn't see record "items",{"id":101,"myColumn":"x"}:
Couldn't find items with {"id":101,"code":"x"}

As you can see, the id of the new record is 101, because there are already 100 items in the db dump, but what it is strange is that the $this->info() in the command prints

total: 100 updated: 100

as if the database used inside the test and the one used inside artisan are different.

Moreover, if at the end of the test I try to grab the added record, and print it, as shown in the following snippet

public function doTheTest(AcceptanceTester $I)
{
    /* ... */

    $item = $I->grabRecord('items', [
        'id'       => $id,
    ]);

    \Codeception\Util\Debug::debug($item);
}

and run the codecept run acceptance --debug command, I get the added record

stdClass Object
(
    [id] => 101
    [myColumn] =>
)

I'm very confused, because the there is a single database, but I'm surely misunderstanding something important here.

Could anyone give me a help?

Thank you very much,


Solution

  • The issue is that every query using Laravel4 module is run in a transaction that, by default, will be rolled back at the end. If you take a look at the Config section of Laravel4 documentation it states

    cleanup: boolean, default true - all db queries will be run in transaction, which will be rolled back at the end of test.

    You can check this if you restart the MySQL server (in which case, when you run the tests again you'll still see the id 101), or take a look at MySQL logs that will have an entry like the following for each test:

    150417 23:24:24 2 Connect root@localhost on laravel-test
    2 Prepare set names 'utf8' collate 'utf8_unicode_ci'
    2 Execute set names 'utf8' collate 'utf8_unicode_ci'
    2 Close stmt
    2 Query START TRANSACTION
    2 Prepare insert into items (myColumn) values (?)
    2 Execute insert into items (myColumn) values ('')
    2 Close stmt
    2 Query ROLLBACK
    2 Quit

    To fix this, you need to configure the option cleanup of Laravel4 module in your codeception.yml file, like this:

    modules:
        config:
            Laravel4:
            cleanup: false