Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 exist.
/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.

like image 563
keithjgrant Avatar asked Mar 22 '10 22:03

keithjgrant


People also ask

What is stub in PHPUnit?

Stubs are used with query like methods - methods that return things, but it's not important if they're actually called. $stub = $this->createMock(SomeClass::class); $stub->method('getSomething') ->willReturn('foo'); $sut->action($stub);


1 Answers

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;
}
like image 50
pinkgothic Avatar answered Sep 17 '22 21:09

pinkgothic