How To Test Calls Made By An Object

[An application of MockObject.]

Question: Given that I'm using a typical "XUnit" automated regression TestingFramework, how can I test a component whose primary purpose is to call other components?

Example: I'm writing a simple file reader, that reads lines from a text file and passes the lines, one a time to another component that processes them. This is done so that I can introduce filters. Also, the receiving component can call back to redirect the current line and/or all following lines to another component for further processing. Also, each receiving component should receive an "OnStart" call before receiving any lines, and an "OnEnd" call after the last line.

Issue: I can make test fixtures that make calls and test results, but I'm not interested in checking the calls I make to the component; I'm most interested in ensuring that it makes the necessary calls to the next component(s). How can I test that it makes the right calls?

I want to ensure that each receiving component always receives one an only one OnStart call, zero or more "here's one line" calls, and then one and only one OnEnd call.

And, when the component that processes header lines redirects all further input to the appropriate component to process detail lines, I have to be sure that no lines are lost or sent to the wrong component.

Solution: Write test stub classes to receive calls from the "file reader" class.

Internal state machines in the stub classes can ASSERT that OnStart, "here's one line" and OnEnd calls are done in the right order.

Stub classes can contain simplistic logic like "accept N lines, and then redirect remaining lines to instance X." The test fixture will set up all the instances in advance, and configure each with instructions as to what it should do.

Stub classes can log the calls they receive to an internal collection/vector/list that can be verified by the test fixture after all calls have been made.


[This seems to be an example of the MockObject pattern.]

[Another similar example is UnitTestDelegator, by DaveWhipp]



(Aside: Why have the complexity of a text file reader class? Because I'm also accepting "files" from HTTP POST commands, database records and interactive clients. So I really have several different and interchangeable "line reader" classes. -- JeffGrigg)


I do this slightly differently. I use a log adapter (made out of a TeeAdapter and a journal) between parties. This allows me to test the system inline and with binding at any time. The major issue is synchronization on the transaction log when dealing with independent threads so an asynchronous logging mechanism is desirable (use a high resolution timestamp). This can also detect race conditions, if you can get one to happen.

Java's dynamic proxies might work well for this, allowing an adapter factory to take the strain, though I am suspicious of their performance since they appear to use reflection internally.

JeffG, I have a question about your system above. Which object controls the file reading process?

JeffGrigg responds...

The "file reader" class controls the program flow. Abstracted...

  class FileReader? : private IReaderCallback {
    FileReader?(String sFileName, IRead parser) {
      TextFile? f;
      f.open(sFileName);
      parser.OnStart?(this);
      while (String line = f.readLine())
        parser.ProcessLine?(this, line);
      parser.OnEnd?(this);
      f.close();
      }
    ...
  }
(Then things get more complex, with the ability to abort the process, redirect lines to other processors, and reject individual lines -- causing them to be written to a standard reject file.)

Yes, I could treat this more like a stream.

But I elect to treat it more like a "text file reading framework" because...

And anyway, this is a simple example of something I first hit in an N-tier object persistence framework. But it's harder to explain the framework, derived from "Designing Solutions with COM+ Technologies" ISBN 0735611270 -- JeffGrigg


[CategoryMockObjects]


EditText of this page (last edited July 23, 2003) or FindPage with title or text search