phplaraveltestingmocking

Cannot mock partial Log facade in Laravel


I'm trying to mock laravel Log. This is my code:

public function test_process_verify_card()
{
    Log::shouldReceive('error')->once();
    Log::makePartial();
    $class = new MyClass();
    $class->myFunction();
}

This is MyClass look like:

class MyClass
{
    public function __construct()
    {
        $this->logger = Logg::channel('test');
    }

    public function myFunction()
    {
        // ... some logic
        $this->loggger->error('Errror!!');
    }
}

When I run test this test case, it throw error

Call to a member function runningUnitTests() on null

  at vendor/laravel/framework/src/Illuminate/Log/LogManager.php:568

I tried to debug this error by putting dd() in LogManager class

    protected function parseDriver($driver)
    {
        $driver ??= $this->getDefaultDriver();
        dd($this->app); // <--- This is my code
        if ($this->app->runningUnitTests()) {
            $driver ??= 'null';
        }

        return $driver;
    }

But it show that $this->app is not null.

I've tried mock facade Date before and it works fine.

I want to test that myFunction executes logging action. Is this correct way to do it?

Update

I also tried to mock it through partialMock() function:

    public function test_process_verify_card()
    {
        $this->partialMock(Logger::class, function (MockInterface $mock) {
            $mock->shouldReceive('error')->once();
        });
        $class = new MyClass();
        $class->myFunction();
    }

But it still not works, it shows error:

  Method error(<Any Arguments>) from Mockery_0_Illuminate_Log_Logger should be called
 exactly 1 times but called 0 times.

  at vendor/phpunit/phpunit/phpunit:98

Solution

  • I would believe the problem why this is not working, is as Log::channel returns a channel on the partial mock. Therefor the mocked instance never receive the error call.

    In Mockery you can easily do chained calls, by using '->' in the shouldReceive() call.

    Log::shouldReceive('channel->error')
        ->once()
        ->andReturn(null);