Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Parallel Testing Mongodb #3268

Discussion options

DatabaseMigration is way too slow for testing takes 3 to 5 seconds for each test, RefreshDatabase wont work if you test a function with transaction since nested transaction is not allowed. Looking at the test technique made by @GromNaN in this package, truncate is being used to make testing faster which I also tried and tested and shave off the time to average 0.2 seconds to execute each test. What could be the reason why DatabaseMigration takes a long time to test?

You must be logged in to vote

I made parallel testing work by providing custom setUpTestCase to create parallel multiple database, which in default it only creates when testcase uses one of this traits,

When testing mongodb, any of this trait doesnt play nice, refreshdatabase you cannot test when controller that has transaction because it disallows nested transaction, truncation foreign key error, transaction same with refreshdatabase issu, DatabaseMigrations 5x slower than without any trait

$databaseTraits = [
 Testing\DatabaseMigrations::class,
 Testing\DatabaseTransactions::class,
 Testing\DatabaseTruncation::class,
 Testing\RefreshDatabase::class,
 ...

Replies: 3 comments 6 replies

Comment options

You're right that deleting/creating a database takes longer than simply deleting all the documents in the collections. However, you still need to consider the insertion of fixtures between each test. Perhaps you could optimize this by dumping/importing with bulkWrite instead of unit insertions.

Another common technique on advanced databases is to use a transaction that is reverted at the end of each test. This also works with MongoDB.

In setUp:

$connection->beginTransaction();

In tearDown:

$connection->rollback();
You must be logged in to vote
4 replies
Comment options

I just use hooks beforeEach and afterEach, this requires to monitor what models needs to be truncated some factories have relations too which also being auto created. What could be the command to nuke the database or is there any way to scan all models that is being extended by MongoDB\Laravel\Eloquent\Model and truncate each of them?

beforeEach(function () {
 $this->seed(DefaultDeviceSeeder::class);
 
});
afterEach(function () {
 User::truncate();
});
Comment options

You don't need to know about the models, you can list all the collections of your database and delete all the documents they contain.

$database = DB::connection('mongodb')->getMongoDB();
foreach($database->listCollectionNames() as $collectionName) {
 $database->getCollection($collectionName)->deleteMany([]);
}

Don't drop the collections if you use indexes or specific collections options like timeseries.

Comment options

You don't need to know about the models, you can list all the collections of your database and delete all the documents they contain.

$database = DB::connection('mongodb')->getMongoDB();
foreach($database->listCollectionNames() as $collectionName) {
 $database->getCollection($collectionName)->deleteMany([]);
}

Don't drop the collections if you use indexes or specific collections options like timeseries.

Hahaha you are right this is better solution

Comment options

You don't need to know about the models, you can list all the collections of your database and delete all the documents they contain.

$database = DB::connection('mongodb')->getMongoDB();
foreach($database->listCollectionNames() as $collectionName) {
 $database->getCollection($collectionName)->deleteMany([]);
}

Don't drop the collections if you use indexes or specific collections options like timeseries.
getCollection is undefined
I replace it with this

 DB::table($collectionName)->truncate();
Comment options

Just additional feedback it might be the fastest to delete collections or truncate but when you try to do parallel testing you might need DatabaseMigration or RefreshDatabase for it to work and make laravel auto create additional databases. But even if you parallel test, the sequential test for truncate is still faster.

Truncate strategy sequential, doesnt work with parallel test

 Tests: 19 passed (21 assertions)
 Duration: 2.52s

RefreshDatabase sequential(this works as long as you are testing doesn't do transactions, since by itself its transaction which mongodb doesnt allow nested one)

Tests: 19 passed (21 assertions)
 Duration: 5.03s

RefreshDatabase parallel

sail artisan test --parallel --processes=4
 ...................
 Tests: 19 passed (21 assertions)
 Duration: 5.58s
 Parallel: 4 processes

DatabaseMigration sequential

 Tests: 19 passed (21 assertions)
 Duration: 29.14s

DatabaseMigration parallel

sail artisan test --parallel --processes=4 
 ...................
 Tests: 19 passed (21 assertions)
 Duration: 24.90s
 Parallel: 4 processes
You must be logged in to vote
2 replies
Comment options

I will revisit this result once I written more assertions and test, and retest the parallel if it drastically improves if there are more test.

Comment options

https://laravel.com/docs/11.x/dusk#reset-truncation
DatabaseTruncation trait exist but its not supported by mongodb

 Method MongoDB\Laravel\Schema\Grammar::compileDisableForeignKeyConstraints does not exist.
 at vendor/laravel/framework/src/Illuminate/Macroable/Traits/Macroable.php:115
 111▕ */
 112public function __call($method, $parameters)
 113▕ {
 114if (! static::hasMacro($method)) {
 ➜ 115throw new BadMethodCallException(sprintf(
 116'Method %s::%s does not exist.', static::class, $method
 117▕ ));
 118▕ }
 119
Comment options

I made parallel testing work by providing custom setUpTestCase to create parallel multiple database, which in default it only creates when testcase uses one of this traits,

When testing mongodb, any of this trait doesnt play nice, refreshdatabase you cannot test when controller that has transaction because it disallows nested transaction, truncation foreign key error, transaction same with refreshdatabase issu, DatabaseMigrations 5x slower than without any trait

$databaseTraits = [
 Testing\DatabaseMigrations::class,
 Testing\DatabaseTransactions::class,
 Testing\DatabaseTruncation::class,
 Testing\RefreshDatabase::class,
 ];

This doesnt require any traits, and handles dropping database, in mongodb it auto creates database once there is a new write. This is the fastest method of testing and shaves off duration

 private function customParallelTestSetup(): void
 {
 if (config('app.env') === 'testing') {
 ParallelTesting::setUpTestCase(function (TestCase $testCase) {
 $default = config('database.default');
 $database = DB::getConfig('database');
 $token = ParallelTesting::token();
 $testDatabase = "{$database}_test_{$token}";
 config()->set(
 "database.connections.{$default}.database",
 $testDatabase,
 );
 DB::purge();
 ParallelTesting::callSetUpTestDatabaseCallbacks($testDatabase);
 });
 // Executed when a test database is created...
 ParallelTesting::setUpTestDatabase(function (string $database, int $token) {
 Artisan::call('db:seed');
 });
 ParallelTesting::tearDownTestCase(function (int $token, TestCase $testCase) {
 //Slower but cleans up everything
 // DB::getDatabase()->drop();
 // This cleanup makes my test lowered from 50seconds to 12 seconds
 foreach (DB::connection('mongodb')->getDatabase()->listCollections() as $collection) {
 $name = $collection->getName();
 $type = $collection->getType();
 // Skip system collections
 if (str_starts_with($name, 'system.')) {
 continue;
 }
 if ($type === 'view') {
 continue;
 }
 DB::table($name)->truncate();
 }
 });
 }
 }

For sequential testing
use beforeEach global hook in pest, then check if Parallel token exist, if it doesnt exist its a sequential test not parallel test then do your cleanup

$token = ParallelTesting::token();
if(!$token)
{
//Cleanup here
}

You must be logged in to vote
0 replies
Answer selected by masterbater
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /