1
votes

Pretty new to PHP, so forgive me if I missed something obvious.

I'm currently using Lumen (which is basically a basic Laravel, with only core dependencies) and Eloquent to manage persistent entities.

I want to write a Unit Test where I need to call a function which, at some point do an Eloquent query like this :

$errorMailRecipients = User::where('id_role',  1)
        ->where('some_value', 1)
        ->whereNotNull('email_addr');

My User.class, is a basic Eloquent entity :

    class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
    {
       use Authenticatable, Authorizable, Eloquence, Mappable;

       const CREATED_AT = 'creation_date';
       const UPDATED_AT = 'modification_date';

       /** Eloquent model configuration */
       protected $connection = 'database_name';
       protected $table = 'user';
       protected $primaryKey = 'id';

      // other stuff mapping, functions, etc ...
}

So, I created a test and trying, with Mockery, to mock this specific query :

class ImportTest extends TestCase
{

    public function test_should_send_error_mail_when_receiving_empty_request()
    {
        // Given
        $controller = new ImportController();

        $mockedRecipients = // <- some Collection containing static data
        $mock = Mockery::mock(User::class, function ($mock) use ($mockedRecipients) {
            $mock->shouldReceive('where')->once()->andReturn($mockedRecipients);
        });
        $this->app->instance('App\Models\User', $mock);

        // When
        $controller->importDevices(new Request());

        // Then
    }
 }

I tried many variants, and to decompose mocking into multiple lines :

    $mock = Mockery::mock(User::class);
    $this->app->instance('User', $mock);
    $mock->shouldReceive('where')->once()->andReturn($mockedRecipients);

Also tried to remove the ->once(), and read the following guides :

But so far, I kept having the following error when executing PHP Unit test :

Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] Connection refused ...

Because, yeah, I don't have local database and it's still trying to hit it. Any thoughts ? Thank you

1
I would call my self an expert in Mocking and testing a laravel project seen it in multiple professional projects. It is a waste to mock query functionality, instead i would suggest using Sqllite memory db for it? if you wanna go that route i can show you. Usually people just setup their ci environment to run mysql tbh these days and run it local when testing locally.mrhn
And the reason it is not working is due to it being called staticallymrhn
Ok thanks for your insights :) I come from the Java environment, and I'm very used to mock database queries with mockito library. I ran across multiple answers like yours, and I don't really agree. For Integration Testing, I'm totally ok to run an in memory db, to put in sets of data and test a feature from the endpoint, to the expected result. But, in my case, I want to test specific little pieces of code related to one function.Alex
It depends on the efficiency of the mocking lib, but to my mind, it's far more lightweight to just mock a minimum response (like 2 object with few fields) instead of running a DB, insert data, cleanup data, etc ...Alex
Laravel has features for running transactions for the cleanup and or just use Sqlite, just my two cents. I can make your code work, but will do it later in the day :)mrhn

1 Answers

1
votes

I found what I was doing wrong, thanks to @mrhn ;)

Mockery allow using aliases for mocking public static functions, I wasn't aware there would be a difference at first :)

Just replacing Mockery::mock('\App\Models\User'); by Mockery::mock('alias:\App\Models\User'); did the trick.

Thus, it's not recommender to use this, so I guess I'll keep learning during my tests writings. As we will soon have a CI chain using directly MySQL instancied db to run against our app, and as @mrhn statement goes this way, I will consider doing real db calls in future.