1

Normally with Drupal 8 I can use dependency injection like this:

<?php 

class ExampleController extends ControllerBase {

  private $http_client;

  public function __construct(HttpClient $http_client) {
    $this->http_client = $http_client;
  }

  public function create(ContainerInterface $container) {
    $http_client = $container->get('http_client');
  }

  public function getResource() {
     $response = $this->http_client->get('http://biebertunes.com?q=selena');
     return $response->getBody();
  }

}

I can then use the $http_client property's methods.

Now in my testing class I'm already extending the UnitTestCase class, so I don't have access to the create method for dependency injection.

<?php 

class ExampleTest extends UnitTestCase {   
  public function testGetResource() {
    $mock = $this->getMock('\Drupal\Example\ExampleController');
  }
}

Now when I run my unit tests it complains that I don't have the $http_client parameter. So how can I inject $http_client into the unit test without the create method?

1 Answer 1

4

I have few thoughts about this situation:

  1. Controllers should not contain business logic, services should. Therefor unit testing a controller is not a good idea in the first place.
  2. If you are unit testing your controllers "getResource" method, why do mock the controller itself? You should mock the HttpClient. (that's the strong part of DI)
  3. First you mock the classes which your constructor needs as arguments. Then you just create your object, which will you test like this: $example_controller = new ExampleController($http_client_mock); And that's it.
6
  • 1
    You're right, of course, about the whole business-logic-in-a-controller - but to be fair, which part of the OP's code would you rather see in a service? Surely creating a new service simply to wrap around the one-line call to the other service (http_client) would be massive overkill here, wouldn't it? I mean, how far does that go? Does the new service need another service to wrap it in another context? Personally I think this is where best practice and the usual advice gets a bit murky. Good answer though
    – Clive
    Jul 21, 2016 at 12:01
  • 1
    I agree with you! As you said he has only a one-line wrap.. so why do you wan't to test it in the first place? This method is much more like a facade here, and does nothing on it's own.
    – ssibal
    Jul 21, 2016 at 12:04
  • 1
    Actually yeah, you're totally right :) The OP should be satisfied that the HTTP client service works by using its tests, rather than essentially adding their own version of it. If they want to make sure the end-to-end works, an acceptance test would probably be more appropriate
    – Clive
    Jul 21, 2016 at 12:17
  • Hey there. Agreed about there being no point in testing that method but it was just there as more of an example. Your answer helped me get on the right track. I also had to use an additional step of implementing the setUp() method and adding the mock objects to the service container via \Drupal::setContainer(). Then I was able to pass the mock objects into my class that I'm testing. Thanks!
    – chap
    Jul 22, 2016 at 6:27
  • It will help future people's of you post a full answer with code answering the question as you posed it
    – Jonathan
    Jul 22, 2016 at 10:15

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.