We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Mocking Requests for unit tests

I am writing unit tests and most of them are working. However, I am having trouble with one thing, which I can't quite understand the difference.

In order to tests calls through the application's router/controller setup, I mock the \Phalcon\Http\Request object like so:

    public function testCreateSingle() {
        $_SERVER["REQUEST_METHOD"] = "POST";
        $_SERVER["REQUEST_URI"] = "logs";
        $_GET["_url"] = "/logs";

        $raw = '{"application": "Test Application","type":"CRITICAL","subject":"test","details":"my details"}';

        $mock = $this->getMock("\\Phalcon\\Http\\Request", array("getRawBody"));
        $mock->expects($this->once())
             ->method("getRawBody")
             ->will($this->returnValue($raw));

        $this->di->set('request', $mock, true);
        include("../app.php");
        $this->expectOutputString('{"success":true,"response":{"result":"success"}}');
}

The problem I am having is, this only works when I do "$this->di->get('request')" in the controller instead of "$this->request".

Any suggestions?

Providing you're using Phalcon\DI\FactoryDefault the only thing that comes to my mind is that the controller should extend one of:

  • Phalcon\Mvc\Controller
  • Phalcon\DI\Injectable where at present it would only be implementing Phalcon\DI\InjectionAwareInterface (a guess)

If it already does, it might be bug worth checking on Github so someone else can confirm. By the look of your code it looks like you're writing integration/functional tests, not unit tests.

edited Feb '17

I took a slightly different approach to do this.

I created a RequestMock:

<?php
use Phalcon\Http\Request;

class RequestMock extends Phalcon\Http\Request
{
    /**
     * @var string|null Holds the raw body to be used for tests
     */
    static protected $raw_body = null;

    /**
     * Override the method to allow us to get a raw body, set for tests
     *
     * @return string
     */
    public function getRawBody()
    {
        return self::$raw_body;
    }

    /**
     * Sets the raw body to be used in tests
     *
     * @param string $raw_body The raw body to be set and used for tests
     */
    public function setRawBody($raw_body)
    {
        self::$raw_body = $raw_body;
    }
}

Then part of my bootstrap.php file, I added:

$di->set('request', new RequestMock());

Then in your test, you can do something like:

Di::getDefault()->get('request')->setRawBody(json_encode($request_array));
edited Mar '17

Hi

Some methods are not mockable like this as they are declared "Final".

I never understood why developers create final methods as it is always usefull to be able to extends and it create issues like this.

Anyway I had to proceed like this with phpunit to mock 2 methods. This is dirty but it works...

$mock = new class($method, $uri) implements RequestInterface { private $mockMethodValue; private $mockUriValue;

        public function __construct($method, $uri)
        {
            $this->mockUriValue = $uri;
            $this->mockMethodValue = $method;
        }
        public function getMethod()
        {
            return $this->mockMethodValue;
        }
        public function getUri()
        {
            return $this->mockUriValue;
        }

        public function get($name = null, $filters = null, $defaultValue = null) {}
        public function getPost($name = null, $filters = null, $defaultValue = null) {}
        public function getQuery($name = null, $filters = null, $defaultValue = null) {}
        public function getServer($name) {}
        public function has($name) {}
        public function hasPost($name) {}
        public function hasPut($name) {}
        public function hasQuery($name) {}
        public function hasServer($name) {}
        public function getHeader($header) {}
        public function getScheme() {}
        public function isAjax() {}
        public function isSoapRequested() {}
        public function isSecureRequest() {}
        public function getRawBody() {}
        public function getServerAddress() {}
        public function getServerName() {}
        public function getHttpHost() {}
        public function getPort() {}
        public function getClientAddress($trustForwardedHeader = false) {}
        public function getUserAgent() {}
        public function isMethod($methods, $strict = false) {}
        public function isPost() {}
        public function isGet() {}
        public function isPut() {}
        public function isHead() {}
        public function isDelete() {}
        public function isOptions() {}
        public function isPurge() {}
        public function isTrace() {}
        public function isConnect() {}
        public function hasFiles($onlySuccessful = false) {}
        public function getUploadedFiles($onlySuccessful = false) {}
        public function getHTTPReferer() {}
        public function getAcceptableContent() {}
        public function getBestAccept() {}
        public function getClientCharsets() {}
        public function getBestCharset() {}
        public function getLanguages() {}
        public function getBestLanguage() {}
        public function getBasicAuth() {}
        public function getDigestAuth() {}

    };

    return $mock;
}

I took a slightly different approach to do this.

I created a RequestMock:

<?php
use Phalcon\Http\Request;

class RequestMock extends Phalcon\Http\Request
{
   /**
    * @var string|null Holds the raw body to be used for tests
    */
   static protected $raw_body = null;

   /**
    * Override the method to allow us to get a raw body, set for tests
    *
    * @return string
    */
   public function getRawBody()
   {
       return self::$raw_body;
   }

   /**
    * Sets the raw body to be used in tests
    *
    * @param string $raw_body The raw body to be set and used for tests
    */
   public function setRawBody($raw_body)
   {
       self::$raw_body = $raw_body;
   }
}

Then part of my bootstrap.php file, I added:

$di->set('request', new RequestMock());

Then in your test, you can do something like:

Di::getDefault()->get('request')->setRawBody(json_encode($request_array));