According to the "Database Testing" documentation I can reset the database after each test (first option). Second option is to run test using Transactions. It seems a better approach to me, but if I want to run with transaction, the migration does not run.
Is there any way to run the migration once for all the test process?
In other words, I want to run the migration, run every tests with transaction, then rollback. I tried with what the documentation says, but I think something is missing.
Wrangled with this for a while today and running migrations in conjunction with migrations seems to do the trick. A snapshot of my test is as follows:
<?php
namespace Tests\Browser;
use App\User;
use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class DefaultTest extends DuskTestCase
{
use DatabaseMigrations, DatabaseTransactions;
/**
* A Dusk test example.
*
* @return void
*/
public function test_something()
{
//Add test stuff here
}
}
I've got a couple of factories in my actual test and they seem to run through the migrations with the data destroyed after the test as expected.
It is not possible to run DatabaseTransactions in combination with dusk for the moment.
https://github.com/laravel/dusk/issues/110
The creation of the user record and the use of it in the browser are
done in two different processes. This means the created user is part
of a database transaction which is not committed and thus not
accessible by the browser process.
Database migrations work. So you should use those. Also make sure you run a seperate testing database so you don't mess with your production/development database.
https://laravel.com/docs/5.4/dusk#environment-handling
To force Dusk to use its own environment file when running tests,
create a .env.dusk.{environment} file in the root of your project.
For example, if you will be initiating the dusk command from your
local environment, you should create a .env.dusk.local file.
When running tests, Dusk will back-up your .env file and rename your
Dusk environment to .env. Once the tests have completed, your .env
file will be restored.
The provided answer works because DatabaseMigrations work. The use DatabaseTransactions
is not relevant.
From what I understand, I don't think transactions can ever work when using dusk, as each browser request in dusk creates a separate instance of your laravel app.
Previously, phpunit would create a new application in memory as part of the process (in the setUp
/ createApplication
method), then test against that testing application, then destroy it and set up the next one. As such, the transactions can be wrapped around (or just inside) the create and destroy parts of that application before it starts up a new database connection for the next test.
With dusk, it's real end-to-end testing (including a browser, faked user interaction, the routing on your local machine, etc.), which means it is not all contained within the environment that your tests are running in, like they usually are in phpunit.
Dusk does the following:
- Copies your
.env.dusk.*
and launches the chromedriver (or whatever selenium-like thing you use)
- Triggers a phpunit shell command (ie. new command, new process)
- The phpunit command runs your dusk tests, which each open a browser window and makes requests (each request starting a new php-fpm and php process (for nginx)) - just as though you were making those requests yourself. Each of them have separate connections to the db, and so cannot interact with each others' transactions.
It's also worth noting that the DatabaseTransactions
trait is in the Foundation package, not the Dusk package, so it's not build / packaged with Dusk in mind.
This also explains why in-memory sqlite doesn't work with dusk, as one process does not have access to another process' memory.
Here is a very portable and reusable way:
abstract class DuskTestCase extends BaseTestCase {
...
/**
* @param int $batchCounter
* @param string $className
* @param int $threshold
*/
public function refreshDb(&$batchCounter, $className = '', $threshold = 0) {
if ($batchCounter <= $threshold) {
//TODO: Here is where you'll want to run migrations and seeds and whatnot.
$batchCounter++;
$this->consoleOutput(trim($className . ' called refreshAndSeedTestingDb and $batchCounter++. $batchCounter=' . $batchCounter));
}
}
/**
* @param string $msg
*/
public function consoleOutput($msg) {
Log::debug($msg);
$output = new \Symfony\Component\Console\Output\ConsoleOutput();
$output->writeln($msg);
}
Then in each test file:
class ExampleBrowserTest extends DuskTestCase {
protected static $countDbRefreshed = 0;
public function setUp() {//runs before every test function in this class
parent::setUp();
$this->refreshDb(self::$countDbRefreshed, __CLASS__); //inside uses a property to run only once per class
}
...