24

i'm new to phpunit and have read the documentation on mock objects but it isn't very clear.

I am trying to write a simple test that asserts a method within a class is called. With the following code, i am testing that when the Client::exchangeArray is called, a call is made to Client::getInputFilter.

class Client implements InputFilterAwareInterface
{

public function getInputFilter() {
    if(!$this->_inputFilter){
        $inputFactory = new InputFactory();
        $inputFilter = new InputFilter();

        $inputFilter->add($inputFactory->createInput(array(
            'name' => 'id',
            'required' => true,
            'filters' => array(
                array(
                    'name' => 'Int'
                )
            )
        )));

        $inputFilter->add($inputFactory->createInput(array(
            'name' => 'name',
            'required' => true,
            'filters' => array(
                array(
                    'name' => 'StripTags'
                ),
                array(
                    'name' => 'StringTrim'
                ),
                array(
                     'name' => 'StripNewLines'      
                ),
                array(
                    'name' => 'Alpha'
                )
            ),
            'validators' => array(
                array(
                    'name' => 'StringLength',
                    'options' => array(
                        'encoding' => 'UTF-8',
                        'min' => 2,
                        'max' => 100
                    )
                )
            )
        )));

        $inputFilter->add($inputFactory->createInput(array(
            'name' => 'surname',
            'required' => true,
            'filters' => array(
                array(
                    'name' => 'StripTags'
                ),
                array(
                    'name' => 'StringTrim'
                )
            ),
            'validators' => array(
                array(
                    'name' => 'StringLength',
                    'options' => array(
                        'encoding' => 'UTF-8',
                        'min' => 2,
                        'max' => 100
                    )
                )
            )
        )));

        $inputFilter->add($inputFactory->createInput(array(
            'name' => 'email',
            'required' => false,
            'filters' => array(
                array(
                    'name' => 'StripTags'
                ),
                array(
                    'name' => 'StringTrim'
                )
            ),
            'validators' => array(
                array(
                    'name' => 'StringLength',
                    'options' => array(
                        'encoding' => 'UTF-8',
                        'min' => 2,
                        'max' => 150
                    )
                ),
                array(
                    'name' => 'EmailAddress'
                )
            )
        )));

        $this->_inputFilter = $inputFilter;
    }
    return $this->_inputFilter;
}

public function exchangeArray($data){
    $inputFilter = $this->getInputFilter();
    $inputFilter->setData($data);
    if(!$inputFilter->isValid()){
        throw new \Exception('Invalid client data'); 
    }

    $cleanValues = $inputFilter->getValues();

    $this->_id = (isset($cleanValues['id']) ? $cleanValues['id'] : null);
    $this->_name = (isset($cleanValues['name']) ? $cleanValues['name'] : null);
    $this->_surname = (isset($cleanValues['surname']) ? $cleanValues['surname'] : null);
    $this->_email = (isset($cleanValues['email']) ? $cleanValues['email'] : null);
    }        
}

Here is my test case:

public function testExchangeArrayCallsInputFilter(){
    $data = array('id' => 54,
            'name' => 'john',
            'surname' => 'doe',
            'email' => '[email protected]'
    );

    $mock = $this->getMock('Client', array('exchangeArray'));
    $mock->expects($this->once())
         ->method('getInputFilter');
    $mock->exchangeArray($data);
}

...and i'm getting the following error:

Expectation failed for method name is equal to when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times.

Where am i going wrong?

1 Answer 1

23

It all depends on what you want test and what you want mock. Basing on the name of your test I assume that you want test exchangeArray method.

The getMock method takes as second argument names of methods that you want mock. It means that they will never be called.

So, if you want to test exchangeArray method and mock getInputFilter you should pass "getInputFilter" in second argument, like below:

$mock = $this->getMock('Client', array('getInputFilter'));
$mock->expects($this->once())
     ->method('getInputFilter');
$mock->exchangeArray($data);

But be careful. You didn't tell your mock to return anything, so it will return null value. That means that you'll get a fatal error on the second line of exchangeArray method (trying to call a method on a non-object). You should prepare some faked filter object to deal with that, eg:

// $preparedFilterObject = ...
$mock = $this->getMock('Client', array('getInputFilter'));
$mock->expects($this->once())
    ->method('getInputFilter')
    ->will($this->returnValue($preparedFilterObject);
$mock->exchangeArray($data);

And if you want to invoke the "real" getInputFilter method - then you just can't mock this method.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the reply. I already have implementations for both methods. I just wanted to check that the methods were being called in a correct order, and then that they are being called with specific parameters. Is there any way to check that methods are being called without mock objects?
No, there isn't. But if you want call the second method you don't need to check if it was actually called because if it wasn't then you don't get $inputFilter object and you test fail anyway.
On the other hand if your test pass and the second method wasn't called (not gonna happen in your case, but in some cases could) you will notice that on code coverage report. And it could mean either that you just dont need this second call OR you have fix your tests. In case your second method set some internal object property, you can also test against this property, to check if the method was called.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.