Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")
I was getting a really strange error while trying to unit test the icalendar library I'm working on. So I wrote a less complex example so that I could eliminate as many variables as I could. The situation is basically like this:
OK, the same test passes without a problem on my work machine. I'll have to check on my laptop when I get a minute to see what version of simpletest I'm running on there... I'm actually not even sure what version of php I have on there. I'll let you know. Thanks a lot!
Take a few steps back and think about this. You're trying to mock a class which has a concrete implementation. Mocking is something you should really be doing on interfaces (think of it as the most generic implementation of that "type"). If your code deals with Books and not just MobyDicks then you really should be mocking Book.
Generally I work in this order of preference:
Interface
Abstract Class
Concrete Class
Unit testing with mock objects becomes so much more straightforward when you're just applying interfaces. It also encourages dependency injection which in turn produces well written and flexible code.
I'd still like to see a really full-blown stand-alone mock object library for PHP. A colleague at work was talking about one of the Java mock object libraries (maybe it was EasyMock) and you essentially put the object into "record mode" then apply it:
Thanks Chris. I think I'm finally starting to understand the concept of mocking, but I'm going to play around with it a little more. Every time I think I understand the concepts, I try writing the tests, I get one or two done, and then I run into something difficult and give up for a while. Well not this time!
While PHP 5's built-in object hinting capabilities are nice, PHP is a dynamically typed language and I'd use it that way! Duck typing (if it acts like a duck, it is a duck) for the win.
Chris, that recording mock concept is really interesting. I wonder, however, how one would specify the return values for the function calls. Edit: It is indeed SimpleMock, as seen here http://www.easymock.org/EasyMock2_2_Documentation.html . Very cool.
$mock = new Mock('ClassName');
$expectation = $mock->expectCall('someMethod');
$expectation->acceptArgs($arrayOfArgs);
$expectation->expectCallCount(2);
To avoid any chance of method name collisions, I'd use a "proxy" object which takes the mock object as a constructor parameter, and has nothing else declared except __call(), __set() and __get(). I then pass this object as the dependency/mock. (This is what I use in Smalltalk, paraphrased in PHP)