How to mock different methods of the same class and how to mock one method with different arguments

I want to mock different methods of the same class and mock one method with different argument values. Using standard PHPUnit.

Mocks in PHPUnit are being created using getMock() method. It belongs to PHPUnit_Framework_TestCase class, that is described in .../PEAR/PHPUnit/Framework/TestCase.php file.
Here is the specification of PHPUnit_Framework_TestCase::getMock() method:

/**
     * Returns a mock object for the specified class.
     * @param  string  $originalClassName
     * @param  array   $methods
     * @param  array   $arguments
     * @param  string  $mockClassName
     * @param  boolean $callOriginalConstructor
     * @param  boolean $callOriginalClone
     * @param  boolean $callAutoload
     * @param  boolean $cloneArguments
     * @return PHPUnit_Framework_MockObject_MockObject
     */
    public function getMock(
       $originalClassName, 
       $methods = array(), 
       array $arguments = array(), 
       $mockClassName = '', 
       $callOriginalConstructor = TRUE, 
       $callOriginalClone = TRUE, 
       $callAutoload = TRUE, 
       $cloneArguments = FALSE)

Here is the specification of PHPUnit_Framework_TestCase::getMockForAbstractClass() method:

/**
    * Returns a mock object for the specified abstract class with all abstract
    * methods of the class mocked. Concrete methods to mock can be specified with
    * the last parameter
    *
    * @param  string  $originalClassName
    * @param  array   $arguments
    * @param  string  $mockClassName
    * @param  boolean $callOriginalConstructor
    * @param  boolean $callOriginalClone
    * @param  boolean $callAutoload
    * @param  array   $mockedMethods
    * @param  boolean $cloneArguments
    * @return PHPUnit_Framework_MockObject_MockObject
    * @since  Method available since Release 3.4.0
    * @throws PHPUnit_Framework_Exception
    */
   public function getMockForAbstractClass(
        $originalClassName, 
        array $arguments = array(), 
        $mockClassName = '', 
        $callOriginalConstructor = TRUE, 
        $callOriginalClone = TRUE, 
        $callAutoload = TRUE, 
        $mockedMethods = array(), 
        $cloneArguments = FALSE)

 

1. Two methods of the same class
Say we have a class MyClass with some method root(), that calculates a root of n-grade that we need to test. Say that root’s grade is taken from getRootBase() method and a precision is set in program configuration and taken from getConfig('precision') method. Thus to test method root() we need to mock two other methods: getRootBase() and getConfig('precision')

class MyClass
{
    function root($x)
    {
        $base = $this->getRootBase();
        $presision = $this->getConfig('precision');
        $root = pow($x, 1/$base);
        return round($root, $precision);
    }

    protected function getConfig($key)
    {
        $aConfig['precision'] = 2;
        return $aConfig[$key];
    }

    protected function getRootBase()
    {
        return 3;
    }
}

Now lets create two method mocks. It’s quite simple.

class MyClassTest extends PHPUnit_Framework_TestCase
{
    function testroot()
    {
        $mockMy = $this->getMock('\MyClass', array('getConfig', 'getRootBase'));

        $mockMy->expects($this->any())
            ->method('getConfig')
            ->with('presision')
            ->will($this->returnValue(2));

        $mockMy->expects($this->any())
            ->method('getRootBase')
            ->will($this->returnValue(2));

        $this->assertEquals(2.83, $mockMy->root(8));
    }
}

2. One method with different argument values
Say we have a multiplication method multi() that needs a type and a modificator from configuration. Thus to test our multi() we need a mock for getConfig() that will simulate it with different arguments. I use for this returnCallback() that analyses arguments and returns the right value.

class MyClass
{
    function multi($a, $b)
    {
        $type = $this->getConfig('multitype');
        $result = 0;
        switch($type)
        {
            case 'strong':
                $modificator = $this->getConfig('multimodificator');
                $result = $a*$b*$modificator;
                break;
            case 'normal':
            default:
                $result = $a*$b;
                break;
        }
        return $result;
    }

    protected function getConfig($key)
    {
        $aConfig['multitype'] = 'strong';
        $aConfig['multimodificator'] = 2;
        return $aConfig[$key];
    }
}

Now lets mock getConfig(), that will return different values depends on argument either ‘multitype’ or ‘multimodificator’.

class MyClassTest extends PHPUnit_Framework_TestCase
{
    function testmulti()
    {
        $mockMy = $this->getMock('\MyClass', array('getConfig'));
        $mockMy->expects($this->any())
                ->method('getConfig')
                ->with(
                    $this->logicalOr(
                        $this->equalTo('multitype'),
                        $this->equalTo('multimodificator')
                    )
                )
                ->will(
                    $this->returnCallback(
                        function($arg)
                        {
                            $retValue = null;
                            switch($arg)
                            {
                                case "multitype";
                                    $retValue = "strong";
                                    break;

                                case "multimodificator";
                                    $retValue = 2; 
                                    break;
                            }
                            return $retValue;    
                        }
                    )
                );
        $this->assertEquals(8, $mockMy->multi(2,2));
    }
}