in Object-Oriented Development, Technology

Interfaces Make Testing Easier

I, along with others, have written on interfaces many times before but recently I had occasion to find a new thing about them that makes them really awesome. A few days ago I was tasked with implementing PHPUnit against a Zend Framework application. This application, like many others, makes use of Zend_Auth, and in doing so makes use of the Zend_Auth_Storage_Session class. The problem with unit testing is that sessions aren’t supported very well, and I ran into all kinds of challenges when I tried to use the existing functionality.

But it turns out that Zend Framework includes an interface, called Zend_Auth_Storage_Interface, which defines the methods that must exist in a storage object. The Zend_Auth_Storage_Session class implements this interface (as it’s required to do by the type hinting in the Zend_Auth::setStorage() method). This meant that I could mock a storage object, give my mocked object to the Zend_Auth::setStorage() method, and avoid all the issues surrounding sessions and unit testing. And what’s more, because the interface defined for me the methods that needed to be implemented, I could have confidence that my tests functioned properly.

The unfortunate thing is that more PHP developers don’t implement interfaces when they could or should. They prefer to implement abstract or concrete classes and extend from there; however, interfaces offer three distinct advantages over inheritance in this case:

  • More than one interface can be implemented, and the resulting object will identify with all the interfaces it implements. By contrast, when extending a class, only one class can be extended by another.
  • Interfaces make mocking objects easy, because there’s no need to override various concrete methods and the interface contains no code. It’s simply the blueprint for what the object will look like.
  • Since an interface already defines the majority of the public API, developers can feel comfortable that the methods they need will be implemented in each mock object they’re given, so long as it implements that interface. This provides fidelity in the tests. There’s nothing worse than discovering a test fails because of what was believed to be a “known” quantity.

Zend Framework implements a number of interfaces which makes developing against it much easier. Since interfaces can be implemented and the objects that implement them are considered objects of that type, users of interfaces find that the applications they’re working with can be that much more flexible.

  1. I just wish that there could be a way to dynamically create a class that implements some interface other than eval() and code generation.

    Generally you have a good point at using interfaces. Most people doesn’t understand what they are meant for, maybe it’s because they lack of knowledge of software engineering.

  2. sozzuka, PHPUnit uses exactly eval() to generate mocks.
    Anyway, usually the choice for testing authentication in ZF applications is to use Zend_Test, which will set Zend_Session to a sort of “testing mode” since it is a singleton and cannot be substituted for now. Things will change in 2.x.

  3. “Since an interface already defines the majority of the public API, developers can feel comfortable that the methods they need will be implemented in each mock object they’re given, so long as it implements that interface.”
    Well not really. If the interface defines just the majority of the public API, you still can’t be sure you’re implementing everything you need.

    An interface should define the entire (public) API. (Or rather, an implementation shouldn’t add any public methods.) Then and only then do they have any kind of use.

  4. You might wanna read up on mock vs. stub objects, there’s more to mock objects than it seems, but usually only stubs are implemented (which is a shame).

  5. @giorgio: I know, but I konsider it as an ugly hack. I don’t like eval – it’s slow, unpredictable and dangerous. That’s way they added lambda’s instead of create_function in 5.3.

    @jory: interface defines a role that a class will fulfil, that’s why you test roles not classes…

Comments are closed.