Collecting Parameter vs overloading addition

Posted on

Problem

I’m reading TDD By Example by Kent Beck (which I highly recommend, if you haven’t read it!) In The xUnit Example, he discusses how to collect the results of multiple tests in a single TestResult. His code looks like this:

# in TestCase
def run(self, result):
    result.testStarted()
    self.setUp()
    try:
        method = getattr(self, self.name)
        method()
    except:
        result.testFailed()

# in TestSuite
def run(self, result):
    for test in self.tests:
        test.run(result)

In short, Beck passes in the currently running TestResult and asks the tests to add their results to it; the TestSuite acts in much the same way. (Beck says the TestSuite is a Composite, and the pattern of passing in the TestResult is called Collecting Parameter.) In other words, somewhere at the top of the call stack, something is creating a single TestResult which gets passed all the way down to the TestCase.

I find the single TestResult object aesthetically distasteful – I generally don’t like sharing state between different components. It smacks of C#’s out keyword (which is not a compliment).

My approach would be to return a TestResult from every TestCase, and allow TestResults to be added to one another:

# in TestResult
def __init__(self, runCount=0, errorCount=0):
    self.runCount = runCount
    self.errorCount = errorCount
def __add__(self, other):
    runCount = self.runCount + other.runCount
    errorCount = self.errorCount + other.errorCount
    return TestResult(runCount, errorCount)

# in TestCase
def run(self):
    result = TestResult()
    result.testStarted()
    self.setUp()
    try:
        method = getattr(self, self.name)
        method()
    except:
        result.testFailed()
    return result

# in TestSuite
def run(self):
    result = TestResult()
    for test in self.tests:
        result += test.run()
    return result

How would you weigh up these two approaches? The state of any given instance of TestResult is now local to the methods. On the other hand, there’s more duplication in mine – every method now creates and returns a new TestResult, which might make writing a new implementation of run harder work.

Solution

One benefit of a collecting parameter is writers can add multiple items to the list. The collecting parameter can descend deeper into the stack and each method is free to add as many items to the list as necessary.

If what was added to the list was restricted to the return value of the method, then it becomes the sender’s responsibility to know how to add the receiver’s return values to the list, and the receiver cannot return another value other than what would have been added to it.

The collecting parameter seems more flexible because any number of receivers may write to it and still be able to return values.

Leave a Reply

Your email address will not be published. Required fields are marked *