开发者

PHPUnit - multiple stubs of same class

I'm building unit tests for class Foo, and I'm fairly new to unit testing.

A key component of my class is an instance of BarCollection which contains a number of Bar objects. One method in Foo iterates through the collection and calls a couple methods on each Bar object in the collection. I want to use stub objects to generate a series of responses for my test class. How do I make the Bar stub class return different values as I iterate? I'm trying to do something along these lines:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar');
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
// populate stubs into `Foo`

// assert results from `Foo->someMethod()`

So Foo->someMethod() will produce data based on the results it receives from the Bar objects. But this gives me the following error whenever the array is longer than one:

There was 1 failure:

1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not e开发者_如何转开发xist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25

One thought I had was to use ->will($this->returnCallback()) to invoke a callback method, but I don't know how to indicate to the callback which Bar object is making the call (and consequently what response to give).

Another idea is to use the onConsecutiveCalls() method, or something like it, to tell my stub to return 1 the first time, 2 the second time, etc, but I'm not sure exactly how to do this. I'm also concerned that if my class ever does anything other than ordered iteration on the collection, I won't have a way to test it.


I'm unfortunately not sure if you can solve your actual question using getMock(), but my experience with getMock() itself is slim.

Only thing I can think of offhand, but not knowing your Bar class, this may not help: The third parameter of getMock() lets you pass constructor arguments (as an array).

I'd create my own mock class extending Bar as a test helper (fancy name for 'just another class that so happens to be used only in tests') that does exactly what I like and inject a series of them into your Foo object. That gives you all the control you'd want, since you can outright replace the methods in question, which getMock() does not do. Of course that also means you're not testing the Bar class in this test, which may not be what you want - though I'd recommend writing a separate test class per tested class anyway, but there are cases where that's unnecessarily purist.

$stubs = array();
foreach ($array as $value) {
    $stubs[] = new MyBarTestHelper($value);
}

That aside, I'm honestly surprised you're only seeing the exception described when you have more than one array element. I've observed that PHPUnit actually expects you to declare any method you want it to be able to track as a getMock() parameter, and will stolidly error out otherwise, since essentially what it does internally is create its own extension of the class, wrapping each method that you expressly declare with logic that lets it determine whether it was called (= adding the method name into a logical list).

So colour me naive (seriously, I probably am, I'm a test newbie, myself), but see if this helps you any:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar', array('GetValue'));
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}


This should satisfy the requirement to return a series of values in order as it's called if you're comfortable with the use of global. It has no idea which Bar is called but if each Bar is called by Foo once in order then it shouldn't be too hard to populate the test data.

$barTestData = array('empty',1,2,3,4,5,6);

function barDataCallback(){
    global $barTestData;
    return next($barTestData);
}


I noticed you have an extra parenthesis after "->method('GetValue')" in your code. Don't know if you copied and pasted that or not.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜