Sign In/My Account | View Cart  
advertisement


Listen Print

A Test::MockObject Illustrated Example

by chromatic
July 10, 2002

People like to find excuses to avoid writing tests for their code. One of the most common goes something like, "It's not feasible to test this, because it relies on external objects" - CGI code, code using the Apache request object, TCP/IP servers, and so on.

The Test::MockObject module makes it much easier to isolate code that uses such objects. For example, if your code uses a CGI object, then you could fiddle with query strings and faked STDIN, trying to persuade CGI.pm to produce testable values. It's easier to use Test::MockObject to create an object that looks and behaves like a CGI object -- but that is completely under your control.

This comes in handy in large software projects, where objects encapsulate tremendous amounts of hidden behavior. If your application follows good design principles by hiding complexity behind well-defined interfaces, then you can replace nearly any component with its interface equivalent. (The internals, of course, are free to change. That's why this is possible.) Often, it's sufficient just to accept certain arguments and to return specific values.

Using a mock object, you can test whether the code in question uses a particular interface correctly. It's possible to do this by hand, but Test::MockObject has several utility methods to add fake methods and verify calls. It integrates with Test::Builder, so it works correctly with Test::Simple, Test::More, and their cousins.

Related Reading

Perl in a Nutshell, 2nd Edition

Perl in a Nutshell, 2nd Edition
By Stephen Spainhour, Ellen Siever, Nate Patwardhan

The Background You Need to Know

I assume that you are already familiar with Test::More. Perhaps you've read Test::Tutorial, which comes with the Test::Simple distribution. You may also have read my earlier introduction to the subject. If not, then you may wish to do so. (My roommate tried it out of order and hurt his head. If you fare any better, then the Perl QA group is interested in your natural talent!)

My example comes from the unit tests for the Everything Engine. I chose it for two reasons. First, it's a project near and dear to my heart. Second, it needs more users, testers and developers. More importantly, it's where I came up with the ideas that led to Test::MockObject.

My colleague on the Engine, Darrick Brown, devised a clever technique he dubbed Form Objects. These are used to bind menu choices to nodes. (Everything in Everything is a node. It's the base unit of data and behavior.) Form objects control the creation of HTML widgets, verify submitted data and ultimately update nodes. They all inherit strongly from Everything::FormObject and operate on node objects, so they're an ideal candidate for mock objects.

Mock Objects

This article focuses on white-box unit testing with mock objects. "White box" testing means that you're allowed and encouraged to look at the internals of the thing being tested. This is scarily possible, with Perl. By contrast, "black box" testing happens when you cannot know the internal details: you just know allowed inputs and the expected outputs. (If you don't know that, then you can't do much testing.)

Unit testing, of course, is testing individual components of the program in isolation, as far as possible. This is different from integration testing, which exercises the program as a whole, and acceptance testing, which explores the desired end-user behavior of the program. No type of testing is better or worse than any other. Done properly, they are complementary: Unit tests are capable of exploring internal behaviors that are difficult to prove with acceptance tests; integration tests demonstrate the interoperability between different components that unit tests usually cannot guarantee.

The points of a mock object is to isolate the units being tested from their dependencies, and to give testers more complete control over the testing environments. This follows from standard programming principles: If you can fake the interfaces your unit relies on, then you can control and monitor its behavior.

Perl makes this particularly easy.

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow