Mocking objects
Moderator: General Moderators
Mocking objects
I'm using simpletest and it's going pretty well, but any time that I need to mock something, I've found that it's much easier to simply extend the class I'm mocking and return whatever value I need to return in the subclass. Is this common practice? Seems like the best "actor" would be a subclass... am I wrong? It seems like you need to fake too much with simpletest's mock objects. I'm just looking for discussion on the subject I guess.
That's the easiest way to do it, but in a TDD world, it's not possible - the objects you are mocking may not exist yet.
Interfaces play a big part of it, but yet again, those interfaces may not exist - the tests help you define those interfaces.
Basically, mock objects help design. Your first tests will only satisfy the highest level of requirements, then after refactoring, and thus creating more tests, you'll be separating behaviour into it's own object. Again, going back to TDD, those separated, specific objects will not exist when you write the tests.
Anyway, enough alcohol fuelled babble.. if the objects you are extending and overriding did not exist, would your tests pass? The answer is a quick flat "no" so we mock an object with a specified interface to ensure that the test will pass even without it's dependencies existing. On a sidenote, I personally prefer to use SimpleTest's mock object API to generate predictable results, rather than manually write them. Creating a series of varying results is easy, not so if I had to create those results myself.
The there is always the case of "What if the mock behaviour I have created is causing the failure? Should I test my mock object?" which pops up.
Interfaces play a big part of it, but yet again, those interfaces may not exist - the tests help you define those interfaces.
Basically, mock objects help design. Your first tests will only satisfy the highest level of requirements, then after refactoring, and thus creating more tests, you'll be separating behaviour into it's own object. Again, going back to TDD, those separated, specific objects will not exist when you write the tests.
Anyway, enough alcohol fuelled babble.. if the objects you are extending and overriding did not exist, would your tests pass? The answer is a quick flat "no" so we mock an object with a specified interface to ensure that the test will pass even without it's dependencies existing. On a sidenote, I personally prefer to use SimpleTest's mock object API to generate predictable results, rather than manually write them. Creating a series of varying results is easy, not so if I had to create those results myself.
The there is always the case of "What if the mock behaviour I have created is causing the failure? Should I test my mock object?" which pops up.
OK, that is very interesting. That is the issue I've always run into with TDD. "How in the world do I test objects when their dependancies don't exist". In this particular situation I had actually given up on testing my library until I had at least base classes for my components. I was under the misconception that you needed an existing class for the Mock::Generate() method to work. I now understand it really does just mock an object. Thanks.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
Just reiterating mostly.
Mock Objects serve a few purposes:
- stand in for as yet non-existing classes; or
- force you to determine a public API/interface sooner rather than later by writing a PHP Interface to mock
- add goals for the non-existing object in terms of interaction, return values, etc. (half the work done right there)
- represent an interaction test since Mock Objects are self-validating
- enforce isolation of the Unit under examination from any other Unit/Resource (though this is not strictly a TDD requirement)
This is probably why I only learned PHPUnit recently - it had no Mock Object support up until recently. And I even think the current incarnation lacks a certain flexibility. In short, I find it uncomfortably limiting. For PHPSpec and PHPT Travis and myself are looking into developing a standalone Mock Object library not bound to one single framework. As soon as Travis gets SimpleTest migrated to subversion at long last probably
. The PHP5 expedition is taking place soon.
The only weakness people tend to see, if viewing from a testing rather than a designing perspective, is that all your tests won't interact with real objects - so this is where you should being in a little Integration Testing or Acceptance Testing (both are almost identical in a small library).
Mock Objects serve a few purposes:
- stand in for as yet non-existing classes; or
- force you to determine a public API/interface sooner rather than later by writing a PHP Interface to mock
- add goals for the non-existing object in terms of interaction, return values, etc. (half the work done right there)
- represent an interaction test since Mock Objects are self-validating
- enforce isolation of the Unit under examination from any other Unit/Resource (though this is not strictly a TDD requirement)
This is probably why I only learned PHPUnit recently - it had no Mock Object support up until recently. And I even think the current incarnation lacks a certain flexibility. In short, I find it uncomfortably limiting. For PHPSpec and PHPT Travis and myself are looking into developing a standalone Mock Object library not bound to one single framework. As soon as Travis gets SimpleTest migrated to subversion at long last probably
The only weakness people tend to see, if viewing from a testing rather than a designing perspective, is that all your tests won't interact with real objects - so this is where you should being in a little Integration Testing or Acceptance Testing (both are almost identical in a small library).
- Chris Corbyn
- Breakbeat Nuttzer
- Posts: 13098
- Joined: Wed Mar 24, 2004 7:57 am
- Location: Melbourne, Australia
You don't have to use simpletest's generator to create mock objects. You can create your own concrete mock object classes which implement that interface you want to mock. It's tedious without a generator though.
I should add that (and I've only started to fully grasp this recently) if you're feeling like setting up your mocks is too tasking then your interfaces are probably more complex than they need to be.
I should add that (and I've only started to fully grasp this recently) if you're feeling like setting up your mocks is too tasking then your interfaces are probably more complex than they need to be.
Usually when I create a test case, I'll write up the interface within the same file of the dependencies; be it an interface or just a blank class declaration with the method / property names like so:
Even when the depndency exists, I still prefer to test like the above. This does have a draw back though, I need to remember that if I change the interface of an object which others use, to update the test cases for all of them. To solve this I also use integrated tests and try to implement FIT/Fitnesse as best I can.
Code: Select all
<?php
require('filecontainingtheclassiamtesting.php');
class Dependency {
public function someMethod() {}
public function someOtherMethod() {}
}
Mock::Generate('Dependency');
class MyTestCase extends UnitTestCase {
public function __construct () {
$reflect = new ReflectionClass($this);
$reflect = $reflect->getParentClass()->getConstrutor()->getName();
$this->$reflect();
}
public function testSomethingOrOther () {
$mock = new MockDependency($this);
$mock->expectOnce('someMethod');
$object = new ObjectBeingTested();
$object->setSomething($mock);
//etc..
}
}
?>- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
I think I am with Jenk here. When I build a Mock Object it is usually either:
1. an interface to a subsystem that I don't want to have to deal with during development (e.g., database)
2. an interface to a class that I am building in parallel with classes dependent on it.
I will write tests for the Mock to verify that it is functioning properly. For #1 I write minimal tests to verify that it works properly. For #2 I write tests just like all the other classes I am developing.
1. an interface to a subsystem that I don't want to have to deal with during development (e.g., database)
2. an interface to a class that I am building in parallel with classes dependent on it.
I will write tests for the Mock to verify that it is functioning properly. For #1 I write minimal tests to verify that it works properly. For #2 I write tests just like all the other classes I am developing.
(#10850)