0

How can I create a mock class (not just a mock object), with a method that, when instantiated will return a predictable value?

In the code below, I am testing a larger concept (accounts->preauthorize()), but I need to mock the object Lookup so that I can get predictable results for my test.

I'm using PHPUnit and CakePHP, if that matters. Here is my situation:

// The system under test
class Accounts
{
    public function preauthorize()
    {
        $obj = new Lookup();
        $result = $obj->get();
        echo $result; // expect to see 'abc'
        // more work done here
    }
}

// The test file, ideas borrowed from question [13389449][1]
class AccountsTest
{
    $foo = $this->getMockBuilder('nonexistent')
        ->setMockClassName('Lookup')
        ->setMethods(array('get'))
        ->getMock();
    // There is now a mock Lookup class with the method get()
    // However, when my code creates an instance of Lookup and calls get(),
    // it returns NULL. It should return 'abc' instead.

    // I expected this to make my instances return 'abc', but it doesn't. 
    $foo->expects($this->any())
        ->method('get')
        ->will($this->returnValue('abc')); 

    // Now run the test on Accounts->preauthorize()
}

1 Answer 1

3

You have several problems here, but the main one is that you are instantiating your Lookup class in the method that requires it. This makes it impossible to mock. You need to pass an instance of Lookup into this method to decouple the dependency.

class Accounts
{
    public function preauthorize(Lookup $obj)
    {
        $result = $obj->get();
        return $result; // You have to return something here, you can't test echo
    }
}

Now you can mock Lookup.

class AccountsTest
{
    $testLookup = $this->getMockBuilder('Lookup')
        ->getMock();

    $testLookup->expects($this->any())
        ->method('get')
        ->will($this->returnValue('abc')); 

    $testAccounts = new Accounts();
    $this->assertEquals($testAccounts->preauthorize($testLookup), 'abc');
}

Unfortunately, I can't test this test, but this should get you moving in the right direction.

Obviously, a unit test for the Lookup class should also exist.

You may also find my answer here of some use.

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

3 Comments

I simplified my scenario so that it made more sense. Turns out I oversimplified it. You do offer a plausible solution for my simplified example, so thanks. But in fact, preauthorize() is a CakePHP controller whose parameters come from the client side and can't accommodate an HttpSocket instance. (What I renamed "Lookup" is actually the CakePHP utility HttpSocket). Argh. Thanks, though.
I've never used cake, but I do hear that it is not too good as far as modern frameworks go. I would emphasise that not being able to isolate your classes to unit test them is a code smell. Obviously, I am not in a position to make a judgement over whether the smell is from your code, or from your framework. Personally, I would be taking a long hard look at my architecture. However, I'm a lone dev and don't have anybody telling how to code or what framework to use. Good luck.
Re-reading your comment. In all frameworks I've come across it is impossible to unit test a controller, it cannot be isolated from its dependancies. I overcome this by moving as much logic as possible out of the controller and into the model or service layers, so that my controller is as skinny as possible. This may be a strategy that you want to consider if you haven't already.

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.