The current mock object landscape.... bleak?
Posted: Mon Mar 10, 2008 3:22 am
I have to admit that the best mock object library currently available (read, complete) in PHP is the one from SimpleTest, but more and more I'm finding myself backed into corners testing implementation details with it. Specifically.... the sequence in which invokations occur.
Say you don't care how many times a mocked method is invoked, but you do care that it's called at least once with the argument "bar". There's no way to do that with SimpleTest except if you know the timing of the method call, which is a huge implementation detail nightmare
I'm a big fan of the interface of jMock where you create a Mockery, then add expectations in a fluent fashion.... and those expectations *do not* have to be in sequence unless specified. Additionally it returns default values which match the method's @return type.
We're also lacking the abaility to "perform actions" on parameters rather than just returning values. Take the scenario where you pass an object as a parameter and expect some invokation on that parameter. There are times when you want your mock to be able to "mock" that invokation rather than returning a value.
After this most recent project I'm working on I've decided I'd like to contribute toward a jMock (ish) clone for PHP. I know Pádraic was working on something, but I don't know what the status of that is.
With a following basic features in place I think a new standalone mock object library for PHP would be a great addition:
* Mock interfaces and maintain any type-hints on method signatures
* Mock classes and maintain the correct type (unless there are "final" methods which can't be overridden)
* Return default values where a docblock comment reveals @return in scope
* Allow invokations in any order
* Allow a sequence to be forced if required
* Using a fluent interface
I've already started a rough (first few hundred lines) code base for what I was planning on calling "YayMock" (as in jMock, but with a Yay!). It doesn't do anything yet but it will look something like this (stolen as closely as possible from jMock).
Then if you want to override the default return value:
If you wish to simply ignore invokations on all methods other than those specified:
If you want to ignore invokations of a particular method call:
If you want to allow a maximum number of calls matching a given invokation:
If you want to specify that calls come in an exact sequence:
If you want to do something other than return a value (or in addition to):
Would anyone be interested?
And would Pádraic be upset or willing to contribute?
I think jMock has the most flexible mock object API I've ever used.
Say you don't care how many times a mocked method is invoked, but you do care that it's called at least once with the argument "bar". There's no way to do that with SimpleTest except if you know the timing of the method call, which is a huge implementation detail nightmare
I'm a big fan of the interface of jMock where you create a Mockery, then add expectations in a fluent fashion.... and those expectations *do not* have to be in sequence unless specified. Additionally it returns default values which match the method's @return type.
We're also lacking the abaility to "perform actions" on parameters rather than just returning values. Take the scenario where you pass an object as a parameter and expect some invokation on that parameter. There are times when you want your mock to be able to "mock" that invokation rather than returning a value.
After this most recent project I'm working on I've decided I'd like to contribute toward a jMock (ish) clone for PHP. I know Pádraic was working on something, but I don't know what the status of that is.
With a following basic features in place I think a new standalone mock object library for PHP would be a great addition:
* Mock interfaces and maintain any type-hints on method signatures
* Mock classes and maintain the correct type (unless there are "final" methods which can't be overridden)
* Return default values where a docblock comment reveals @return in scope
* Allow invokations in any order
* Allow a sequence to be forced if required
* Using a fluent interface
I've already started a rough (first few hundred lines) code base for what I was planning on calling "YayMock" (as in jMock, but with a Yay!). It doesn't do anything yet but it will look something like this (stolen as closely as possible from jMock).
Code: Select all
interface SomeInterface {
/**
* @return Foo
*/
makeFoo($arg);
}
$mockery = new Mockery();
$mockObj = $mockery->mock('SomeInterface');
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')
->one($mockObj)->makeFoo('another arg')
);
$foo1 = $mockObj->makeFoo('another arg'); //returns a Mocked instance of Foo
$foo2 = $mockObj->makeFoo('some arg'); //returns a different Mocked instance of Foo
//This throws some documented type of Exception the xUnit framework can wrap
$mockery->assertIsSatisfied();Code: Select all
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')->will(returnValue($myFoo))
->one($mockObj)->makeFoo('another arg')
);Code: Select all
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')
->ignoring($mockObj)
);Code: Select all
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')->will(returnValue($myFoo))
->ignoring($mockObj)->makeFoo('another arg')
);Code: Select all
$mockery->checking(
Expectations::newInstance()
->atMost(3)->of($mockObj)->makeFoo('some arg')
->one($mockObj)->makeFoo('another arg')
);Code: Select all
$seq = $mockery->createSequence('blah');
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')->inSequence($seq)->will(returnValue($myFoo))
->one($mockObj)->makeFoo('another arg')->inSequence($seq)
);Code: Select all
$mockery->checking(
Expectations::newInstance()
->one($mockObj)->makeFoo('some arg')->will(new MyAction($something))
->one($mockObj)->makeFoo('another arg')
);I think jMock has the most flexible mock object API I've ever used.