Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking PHP functions in unit tests

I'm unit-testing some PHP code with SimpleTest and I've run into trouble. In my tests of a database class I want to be able to set an expectation for PHPs mysql functions. In my tests of a wrapper class for the mail function I want to mock PHPs mail function. These are just some examples.

The point is: I don't (always) want to test if my Mail class sends e-mail, I want to test how it calls the mail function. I want to be able to control what these functions return. I want to be able to test my Database class without needing a database, fixtures and that whole lot.

I've got some experience with testing Ruby code, and Test::Unit and RSpec make it very easy to test code in isolation. I'm new to testing PHP and it feels like I'm testing a lot more than I should need to, in order to get my tests to pass.

Is there a way in SimpleTest or PhpUnit or some other testing framework that makes this possible or easier?

like image 457
avdgaag Avatar asked Aug 07 '09 14:08

avdgaag


People also ask

Is mocking good for unit testing?

Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.

What is mocking in unit test cases?

Mocking is used in unit tests to replace the return value of a class method or function. This may seem counterintuitive since unit tests are supposed to test the class method or function, but we are replacing all those processing and setting a predefined output.

How do you mock in PHP?

To mock a class in PHP you can use the library Mockery. The easiest way is to use mockery by using the static method and pass in the class you want to mock. $twitterMock = \Mockery::mock(TwitterClient::class); We can now use this mock object to change the response of the methods.

Why do we need mocking in unit testing?

Mocking is a way to replace a dependency in a unit under test with a stand-in for that dependency. The stand-in allows the unit under test to be tested without invoking the real dependency.


2 Answers

Not in an automated way. What you can do, is to write your code in a way such that external dependencies are wrapped in objects that are passed in from the outside. In your production environment you'll just wire up the real adapters, but during testing, you can wire it to stubs or mocks.

If you really insist, you can use the runkit extension which changes php's programming model so that you can redefine classes and functions at runtime. This is an external and nonstandard extensions however, so keept that in mind. The defacto standard is a manual approach as I described above.

like image 166
troelskn Avatar answered Nov 07 '22 09:11

troelskn


Here is an interesting article that writes about mocking global php functions. The author proposes a very creative solution to 'Mock' (kind off) the global php functions by overwriting the methods inside the namespace of the SUT (service under test).

Here code from an example in the blog post where the time function is mocked:

<?php

namespace My\Namespace;

use PHPUnit_Framework_TestCase;

/**
 * Override time() in current namespace for testing
 *
 * @return int
 */
function time()
{
    return SomeClassTest::$now ?: \time();
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var int $now Timestamp that will be returned by time()
     */
    public static $now;

    /**
     * @var SomeClass $someClass Test subject
     */
    private $someClass;

    /**
     * Create test subject before test
     */
    protected function setUp()
    {
        parent::setUp();
        $this->someClass = new SomeClass;
    }

    /**
     * Reset custom time after test
     */
    protected function tearDown()
    {
        self::$now = null;
    }

    /*
     * Test cases
     */
    public function testOneHourAgoFromNoon()
    {
        self::$now = strtotime('12:00');
        $this->assertEquals('11:00', $this->someClass->oneHourAgo());
    }
    public function testOneHourAgoFromMidnight()
    {
        self::$now = strtotime('0:00');
        $this->assertEquals('23:00', $this->someClass->oneHourAgo());
    }
}

Not sure if this is a good way to do it but it surely works well and I think it is worth mentioning here. Could be some food for a discussion...

like image 1
Wilt Avatar answered Nov 07 '22 09:11

Wilt