0

I'm trying to test a command where there's a method I want to mock because it makes a call to an external service using Guzzle, but no matter what I try, I can't seem to mock it successfully. It always calls the original method. Any ideas? This is my command (relevant code):

class CommandToTest extends Command
{

    //constants, setup handle and other stuff

    //other methods

    protected function methodToMock()
    {
        //method with the call to external web service that should be mocked
    }

    //other methods
}

Test:

class TestForCommand extends TestCase
{
    //constants etc

    public function testCommandToTest()
    {
        $commandMock = Mockery::mock(CommandToTest::class)->makePartial();

        $commandMock->shouldAllowMockingProtectedMethods()->shouldReceive('methodToMock')
            ->andReturn([
                'Test01' => 'T01',
                'Test02' => 'T02',
                'Test03' => 'T03'
            ]);

        $this->app->instance(CommandToTest::class, $commandMock);

        //other stuff    

        $this->artisan('tools:commandtotest', [//params])
            ->expectsConfirmation(//params ok)
            ->assertOk();
    }

    //assertions
}

But no matter what I try, the real method is always called. How can I make this work? Should I approach it from another angle? Thanks.

1 Answer 1

1

If you are writing tests for the command, you should be mocking the http client so that it returns a mocked response instead of mocking the command itself.

Simplest way to achieve this is to switch to Laravel's Http Client. This http client was built with testing in mind so if you use it, mocking a response would be as simple as

Http::fake([
    // Stub a JSON response for GitHub endpoints...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
 
    // Stub a string response for Google endpoints...
    'google.com/*' => Http::response('Hello World', 200, $headers),
]);

Alternatively, if you must stick with guzzle, then you should inject it or resolve it in your method so that you use dependency injection

class CommandToTest extends Command
{

    //constants, setup handle and other stuff

    //other methods

    protected function methodToMock()
    {
        //method with the call to external web service that should be mocked
        $client = resolve(\GuzzleHttp\Client::class);
        $response = $client->post('something');
    }

    //other methods
}

Then you may inject the mock to the container inside your tests

$this->app->instance(\GuzzleHttp\Client::class, $guzzleMock);
Sign up to request clarification or add additional context in comments.

3 Comments

You are a lifesaver! I had tried both methods (Laravel Http client and mocking the Guzzle client) with no success, but now it worked. I think the key was using this: resolve(\GuzzleHttp\Client::class); I hadn't been using resolve, and I think it has the magic I needed. Thank you!
@MiguelHeredia do not use resolve, use Http Facade, because if the underlying implementation changes, you are done, and when you fake a guzzle call, you are using this
@matiaslauriti I think you misunderstood my answer, or maybe I wasn't clear enough. My suggestion was to use Laravel's Http ideally. In case OP can't use Larave's Http and is forced to use guzzle directly, that's when you should use the resolve method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.