0

I'm still in the process of learning about Laravel and Dependency Injection. I understand the concept, but I don't know how to mock a dependency in this specific case:

MyController.php

use Illuminate\Routing\Controller;    
use MyPackage\Services\ServiceInterface;

class MyController extends Controller{
    protected $service;

    public function __construct(ServiceInterface $service)
    {
        $this->service = $service;
    }    
}

MyServiceProvider.php

use Illuminate\Support\ServiceProvider;

class MyServiceProvider extends ServiceProvider{

    public function register()
    {
        $this->app->bind('MyPackage\Services\ServiceInterface', function ($app) {
            return new MyPackage\Services\ConcreteService(['key'=>'123'], $app->make('GuzzleHttp\Client'));
        });
    }    
}

So, as you can see, I have a controller that requires an instance of ServiceInterface. That instance is being resolved in the ServiceProvider. The constructor of ConcreteService requires a client to perform Http request to an API. This Http is being resolved by the Service container (It will be an instance of Guzzle).

Now, how can I mock this instance of Guzzle on my tests?

The ideal result is doing something like this:

MyTest.php

...
$this->post(route('routeToActionInMyController'), $params);

So, in my tests I just need to hit the route that will be using an specific method of MyController.php but I don't need a "real" Guzzle instance. I just need to mock the response to test if MyController behaves in the expected way (and stores things in the database properly).

How can I instruct the Service Container to inject a Mocked object during tests only? Or am I doing this in the completely wrong way?

Any help will be appreciated.

Thanks in advance

2
  • class MyServiceProvider extends Controller looks like very bad practice. Controllers and service providers are meant to be responsible for completely different things Commented Oct 9, 2017 at 13:56
  • Oh, I'm so sorry, that was a typo Commented Oct 9, 2017 at 13:57

1 Answer 1

3

In your test class:

class TestingSomething extends TestCase {
     protected function setUp() {
          parent::setUp();
          $mockServiceInterface = $this->getMockBuilder(ServiceInterface::class)->getMock();
          $this->app->instance(ServiceInterface::class,$mockServiceInterface);
     }

     public function testPostToRoute() {
        $this->post(route('routeToActionInMyController'), $params);
    }
 } 

This should replace what's already bound in the service container with that mock instance.

Refer to the PHPUnit manual on chapter 9. Test doubles for what you can do with the mock builder and resulting mocks.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your reply @apokryfos. This means that I need to replace the entire binding? I mean, I cannot replace only the Guzzle binding?
If you instantiate guzzle via dependency injection then you can mock the guzzle client. That should work too
I think Guzzle has this out of the box. Check docs.guzzlephp.org/en/stable/testing.html
I think I'm doing that properly. Can you please tell me if it is correct? This is what I'm doing: The constructor of the ConcreteService receives a parameter which must be an instance of Guzzle. And in the Service Provider I'm passing (injecting) that parameter by doing: $app->make('GuzzleHttp\Client')
Yes you're doing that correctly so in your test setup you can do $this->app->bind('GuzzleHttp\Client', function () { /* code from http://docs.guzzlephp.org/en/stable/testing.html here */ })
|

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.