Reputation: 6693
After taking on a project that is eager loading so much that it causes infinite loops in some effected areas, as a need for speed, some of the SQL was written through the getPdo
method.
I now want to write Unit Tests for the associated changed areas and thus Mock the getPdo
to point at the in memory SQLite. I am writing a short test to just see if I can mock the getPdo
method succesfully by just asserting that I have a table my_table
which has no results.
(new \PDO('sqlite::memory:'))->exec('CREATE TABLE my_table (id INTEGER PRIMARY KEY, name TEXT)');
$mock = Mockery::mock('alias:Illuminate\Support\Facades\DB');
$mock->shouldReceive('connection')
->andReturnSelf();
$mock->connection()->shouldReceive('getPdo')
->andReturnUsing(function () {
return new \PDO('sqlite::memory:');
});
DB::swap($mock);
$stmt = DB::connection()->getPdo()->prepare('select * from my_table');
$stmt->execute();
$this->assertEquals([], $stmt->fetchAll(\PDO::FETCH_OBJ));
Running this test is giving me the following error which I cannot seem to understand:
Static method Illuminate\Support\Facades\DB::swap() does not exist on this mock object
I am using Laravel 8.8X.X and usually, the DB::swap
works perfectly fine when changing the underlying interface. How can I get around this?
Update:
Using the built in mocks as suggested, it doesn't find my table.
DB::shouldReceive('connection->getPdo')
->andReturnUsing(function () {
return new \PDO('sqlite::memory:');
});
SQLSTATE[HY000]: General error: 1 no such table: my_table
Update 2:
Mocking the Connection
fixes the swap
issue but I still cannot see my table during a SQL statement:
(new \PDO('sqlite::memory:'))->exec('CREATE TABLE my_table (id INTEGER PRIMARY KEY, name TEXT)');
$connection = Mockery::mock('Illuminate\Database\Connection');
$connection->shouldReceive('connection')->andReturnSelf();
$connection->connection()->shouldReceive('getPdo')
->andReturnUsing(fn() => new \PDO('sqlite::memory:'));
DB::swap($connection);
$stmt = DB::connection()->getPdo()->prepare('select * from my_table');
$stmt->execute();
$this->assertEquals([], $stmt->fetchAll(\PDO::FETCH_OBJ));
SQLSTATE[HY000]: General error: 1 no such table: my_table
Strangely, if I check if the mock is successful by doing:
$connection->connection()->shouldReceive('getPdo')->andReturnUsing(fn() => []);
$this->assertEquals([], DB::connection()->getPdo());
I get a passed test. Putting it back and doing more debug, The exec
seems to be returning 0
when creating the table?
Upvotes: 1
Views: 535
Reputation: 97996
You are mocking the wrong class.
Laravel's facades are static wrappers around an instance of some class, in this case a database connection. In your test, you are replacing the entire static Facade with a mock:
$mock = Mockery::mock('alias:Illuminate\Support\Facades\DB');
Then later, you ask that mock Facade to swap out its underlying instance:
DB::swap($mock);
The mock class has no definition for "swap", so the mocking library complains.
What you wanted to do instead was mock the database connection itself, then tell the real Facade to use that as its instance. I don't have a Laravel project to hand to test, but I believe this is the right class:
$mockConnection = Mockery::mock('Illuminate\Database\Connection');
// Note that this is the real DB class, not a mock!
DB::swap($mockConnection);
There are also built-in helpers for mocking facades.
Upvotes: 1