Jamie Rumbelow
Jamie Rumbelow

Reputation: 5095

SimpleTest Mocking Problem

I'm writing some test cases, and I've got a test case that is using Mock objects. I need to check to see if two class methods are called from another class method. Here's what I've done:

First I generated the Mock:

Mock::generate('Parser');

Then, inside my test I called:

$P = new MockParser();

$P->expectOnce('loadUrl', array('http://url'));
$P->expectOnce('parse');

$P->fetchAndParse('http://url');

My implementation code looks like:

public function fetchAndParse($url) {
    $this->loadUrl($url);
    $this->parse();
}

And the loadUrl and parse() methods definately exist. I'm getting two failures on my tests, both telling me "Expected call count for [loadUrl] was [1] got [0]". I've got no idea what's going on - the methods are being called from that function!

Thanks,

Jamie

Upvotes: 1

Views: 502

Answers (2)

inf3rno
inf3rno

Reputation: 26137

You misunderstood how mocking works. If you use dependency injection to set a helper object in your class, then you can mock your injected object. After that you can simulate the behavior (interface) of the original object. The better way is to mock interfaces, because you can develop without creating any class implementing the current interface.

By your example:

interface UrlLoaderInterface {

    public function load($url);
}

class YourParser {

    protected $urlLoader;
    protected $source;

    public function setUrlLoader(UrlLoaderInterface $urlLoader) {
        $this->urlLoader = $urlLoader;
    }

    public function fetchAndParse($url) {
        $this->loadUrl($url);
        $this->parse();
    }

    public function loadUrl($url) {
        $this->source = $this->urlLoader->load($url);
    }

    public function parse() {

    }

}

Mock::generate('UrlLoaderInterface', 'MockUrlLoader');

class TestYourParser extends UnitTestCase {

    public function testShouldCallUrlLoaderByFetchAndParse() {
        $testUrl = 'http://url';

        $urlLoader = new MockUrlLoader();
        $urlLoader->expectOnce('load', array($testUrl));
        $urlLoader->returns('load', 'source', array($testUrl));

        $parser = new YourParser();
        $parser->setUrlLoader($urlLoader);
        $parser->fetchAndParse($testUrl);
    }

}

Btw. your example is a fail, because method names cannot contain words like 'and', 'or', 'if', etc... A method is allowed to do only one thing. If you use these words then you can be sure that you have a bad designed code.

Upvotes: 1

Praveen Angyan
Praveen Angyan

Reputation: 7265

While my experience has been with mocking frameworks in the .NET world, I think that what you're trying to do is incorrect.

Any mocking framework when asked to create a mock for a class, generates "stubs" for ALL the methods in that class. This includes the method fetchAndParse. So when you are calling fetchAndParse on your mock object $P, the methods loadUrl and parse are NOT called. What you are really doing is calling the "stubbed" fetchAndParse method.

I'm not really experienced in PHP, so I don't want to try and fix your test. Hopefully someone else can do that.

Upvotes: 4

Related Questions