moteutsch
moteutsch

Reputation: 3831

Injecting single-use object into class

I have the following code:

<?php

class X
{
    public function do($url)
    {
        $httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
        $httpRequest->fire();
        // etc.
    }
    // ...
}

In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:

<?php

class X
{
    private $httpRequestClass;

    public function __construct($httpRequestClass = '\HttpRequest\Curl')
    {
        $this->httpRequestClass = $httpRequestClass;
    }

    public function do($url)
    {
        $httpRequest = new $this->httpRequestClass($url, $this->getOptions());
        $httpRequest->fire();
        // etc.
    }
    // ...
}

But this doesn't seem right. Any other ideas?

Upvotes: 2

Views: 124

Answers (2)

moteutsch
moteutsch

Reputation: 3831

The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.

<?php

class X
{
    private $factoryCallback;

    public function __construct($factoryCallback = null)
    {
        $this->factoryCallback = $factoryCallback;
    }

    public function do($url)
    {
        $httpRequest = $this->createHttpRequest($url);
        $httpRequest->fire();
        // etc.
    }

    private function createHttpRequest($url)
    {
        $callback = $this->factoryCallback;
        if (is_callable($callback)) {
            return $callback($url, $this->getOptions());
        }
        return new \HttpRequest\Curl($url, $this->getOptions());
    }
    // ...
}

The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.

Upvotes: 0

dynamic
dynamic

Reputation: 48121

 public function __construct($url, $httpRequestClass = null)
    {
        $this->url = $url;
        if ($httpRequestClass == null) //> Default
           $this->httpRequestClass = new HttpRequest\Curl($this->url);
        else
           $this->httpRequestClass = $httpRequestClass;
    }

so when you are using this class normally just call it with one param

yourClass('your url');

Otherwise pass the istance in the second argument

yourClass('url', new MockedObj);

Of course you should always Inject your dependencies without providing a default object

Upvotes: 1

Related Questions