Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top