Reputation: 1309
I have created the following unit test:
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\User;
use App\Organization;
class UserTest extends TestCase
{
public function testUserHasOwnedOrganization()
{
$user = factory(User::class)->create();
$organization = factory(Organization::class)->create([
'organizer_id' => $user->id,
]);
$this->assertContains($organization, $user->owned_organizations);
}
}
When I run it, I get:
SQLSTATE[HY000]: General error: 1 no such table: users
However, when I open up php artisan tinker
:
>>> factory(App\User::class)->create()
=> App\User {#3048
name: "Margret Armstrong",
email: "[email protected]",
email_verified_at: "2020-05-18 01:22:30",
updated_at: "2020-05-18 01:22:30",
created_at: "2020-05-18 01:22:30",
id: 1,
}
So clearly the factory works and the table exists.
What's going on here?
Thanks,
Upvotes: 10
Views: 17169
Reputation: 425
For me the problem was in migration file, I tried creating table with
public function up()
{
Schema::table
instead of
public function up()
{
Schema::create
Upvotes: 1
Reputation: 710
You are likely testing with the in-memory database (https://en.wikipedia.org/wiki/In-memory_database) without realising it, and it looks like this isn't what you want to do, based upon your question. Easily done, because Laravel's documentation isn't clear that this is Laravel's default phpUnit test config. When your project's testing is configured for in-memory-db testing, the db needs to be built in memory to test against.
Implementing
use RefreshDatabase;
ensures that the db is built in memory if the testing is configured for in-memory-db testing.
How do you switch away from in-memory-db testing to physical db testing? Check your phpunit.xml. You will find environment variables declared as follows:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="sqllite"/> ##<--
## Change this to the default db connection, i.e. "mysql"
<server name="DB_DATABASE" value=":memory:"/> ## <-- here!!
## Change to something else mainly for your benefit, because the
## test-environment should pick up your db from your .env.testing
## You have created a .env.testing, right?
<server name="MAIL_DRIVER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>
Further tip:
You don't need to rebuild the db when you're testing against an already-prepared physical db. You'll see if you study the code in vendor/laravel/framework/src/Illuminate/Foundation/Testing/RefreshDatabase.php that RefreshDatabase exists to re-migrate the db from fresh, whether an in-memory-db or a physical db. In your case, you need only to truncate target tables, which you should be able to do in an extended PHPUnit::setUp() protected method. I recommend extended, because there's a significant amount of setup code in Laravel's TestCase::setUp()
class MyTestClass extends TestCase
{
protected function setUp()
{
$this->doMyDbTableClearDown();
parent::setUp();
}
}
On further investigation, I found the protected array-variable TestCase::$afterApplicationCreatedCallbacks, which appears it could be used to reference the $this->doMyDbTableClearDown(). I haven't used this so far, but it might prove a more elegant solution to the above.
Upvotes: 2
Reputation: 2110
You will need to migrate the DB when you run the test one way or another.
A common way is to utilize the RefreshDatabase
trait.
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\User;
use App\Organization;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function testUserHasOwnedOrganization()
{
$user = factory(User::class)->create();
$organization = factory(Organization::class)->create([
'organizer_id' => $user->id,
]);
$this->assertContains($organization, $user->owned_organizations);
}
}
See if that helps you out.
You can find more information about it here: https://laravel.com/docs/7.x/database-testing
Upvotes: 29