Laravel-mongodb: Use RefreshDatabase create Call to a member function beginTransaction() on null error

Created on 27 Mar 2018  路  12Comments  路  Source: jenssegers/laravel-mongodb

When testing database laravel's documentation suggest using RefreshDatabase trait.
But that cause Error: Call to a member function beginTransaction() on null

question docs

Most helpful comment

Just discovered that in my case, using use DatabaseMigrations; trait, solves my actual problem (reset database after each test) with just one line.

All 12 comments

Did you fixed it? @theminer3746

Would love to know if some of you achieved a solution...

@lscarneiro I created a trait that resets the db


<?php

namespace Tests;

use DB;
use Jenssegers\Mongodb\Schema\Blueprint;

trait RefreshDatabase
{
    /**
     * Set up function.
     */
    public function setUp(): void
    {
        parent::setUp();

        if ($this->hasDependencies()) {
            return;
        }

        $this->dropAllCollections();
    }

    /**
     * Drop all collections.
     */
    protected function dropAllCollections(): void
    {
        $mongo = DB::connection('mongodb');

        foreach ($mongo->listCollections() as $collection) {
            if (starts_with($name = (string) $collection->getName(), 'system')) {
                continue;
            }

            (new Blueprint($mongo, $name))->drop();
        }
    }
}

Thanks @LucasLeandro1204.

I wanted a more "generic" approach, so what I did was create a Trait in app\Traits (new folder in my case) with:

<?php

namespace App\Traits;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Testing\RefreshDatabaseState;

trait RefreshDatabaseTransactionLess
{
    /**
     * Refresh a conventional test database.
     *
     * @return void
     */
    public function refreshTestDatabase()
    {
        if (!RefreshDatabaseState::$migrated) {
            $this->artisan('migrate:fresh', $this->shouldDropViews() ? [
                '--drop-views' => true,
            ] : []);

            $this->app[Kernel::class]->setArtisan(null);

            RefreshDatabaseState::$migrated = true;
        }

    }
}

And in my test class I use both RefreshDatabase and RefreshDatabaseTransactionLess this way:

use RefreshDatabase, RefreshDatabaseTransactionless {
        RefreshDatabaseTransactionless::refreshTestDatabase insteadof RefreshDatabase;
    } 

That tip I got from this StackOverflow post

I consider it a Hack, because it does not solve the original problem, which is execute transactions with this MongoDB Laravel driver.

The code from refreshTestDatabase() inside RefreshDatabaseTransactionLess didn't come out of thin air, it's actually the same code from the original trait, but skipping the call to $this->beginDatabaseTransaction();

Just thinking a little further.

An even cleaner approach is to instead of overriding refreshTestDatabase(), to actually override beginDatabaseTransaction().

Just tested here and it works like a charm!

in RefreshDatabaseTransactionLess trait

<?php

namespace App\Traits;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Testing\RefreshDatabaseState;

trait RefreshDatabaseTransactionLess
{
    /**
     * Begin a database transaction on the testing database.
     *
     * @return void
     */
    public function beginDatabaseTransaction()
    {
        // Nothing! This is on purpose! just skip me!
    }
}

And on the test class:

use RefreshDatabase, RefreshDatabaseTransactionless {
    RefreshDatabaseTransactionless::beginDatabaseTransaction insteadof RefreshDatabase;
}

That way we're achieving the exact "hack" that we want, skip the transaction part (that breaks the test).

I created this Gist containing an example of implementation of the Hack

Just discovered that in my case, using use DatabaseMigrations; trait, solves my actual problem (reset database after each test) with just one line.

@lscarneiro , how to override beginTransaction() method?

@prashant-pokhriyal sorry for the delay.

If you're talking about the specific $connection->beginTransaction(); on RefreshDatabase trait, I would say that this is not the easiest approach because it is a method from a yet another class.

Which behavior are you targetting specifically?

@lscarneiro Actually I'm using a library which internally uses Transactions. Since laravel-mongodb does not have Transaction, it throws an error: Call to a member function beginTransaction() on null. I somehow want to bypass this.

I don't think I have a solution for your case, maybe you could try some help with the library issues page, here in my case I didn't change laravel-mongodb code itself, but rather the code that "depends" on transactions per se.

Any news on this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

geofflancaster picture geofflancaster  路  3Comments

Vasiliy-Bondarenko picture Vasiliy-Bondarenko  路  3Comments

kschethan picture kschethan  路  3Comments

ricardofontanelli picture ricardofontanelli  路  3Comments

viacheslavpleshkov picture viacheslavpleshkov  路  3Comments