Mocking objects

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")

Moderator: General Moderators

Post Reply
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Mocking objects

Post by Luke »

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.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

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.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

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.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

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).
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

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.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

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:

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..
  }
}
?>
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.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

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.
(#10850)
Post Reply