Episode 2 – The InputReader and the start of the Processor

OK, so this stuff is different. Really different. So different that i feel like a TDD rookie all over again. I find myself questioning everything that I do, and wondering if I’m going in the right direction. But it’s fun learning something new…

When I last left you…

When we finished episode 1, we had created a couple of customer tests and had used Interaction Based Testing to drive out the basics of our architecture. In looking back at what we drove out, I wonder about some of those classes. I can see that I have an input side, and processing middle, and an output side, but I see an awful lot of generalization having happened already. I’m going to watch out for this throughout the rest of this exercise. It is possible that this style of writing tests drives you towards early generalization, but I’m pretty sure it is just my unfamiliarity with how to drive code through these tests that is making this happen.

The Input Side

According to the first test I wrote, this is the interface that the input side needs to have:

public interface IInputReader
{
    List<BatchInput> ReadAllInputs();
}

I don’t know anything about a BatchInput yet, or how to read the input lines, but I think I may be about to find out.

So my job now is to drive out how the IInputReader will be implemented by some class. As it turns out, this is really not very interesting. The interaction-based testing that we’ve been doing has been very useful at driving out interactions, but the InputReader seems to stand alone. It is at the very end of the call chain, which means that it doesn’t interact with anything else. This means that state-based tests will do just fine to test this piece of the system.

Here is the first test I wrote for this class:

[Test]
public void InputReaderImplementsIInputReader()
{
    Assert.IsInstanceOfType(typeof(IInputReader), new InputReader(null));
}

I’ve started writing tests like these to force me to make the class I’m writing implement a specific interface. I started doing this because I’ve found myself going along writing a class, knowing full well that it has to implement some interface, but forgetting to actually do it. I end up writing the whole class to the wrong API, and I have to go back and refactor the API to match what it should be. Hence, I write this test now to enforce me implementing the right interface.

Here are the rest of the tests:

[Test]
public void EmptyInputStreamGeneratesEmptyOutputList()
{
    StringReader reader = new StringReader(String.Empty);
    InputReader inputReader = new InputReader(reader);

    List<BatchInput> input = inputReader.ReadAllInputs();

    Assert.AreEqual(0, input.Count);
}

[Test]
public void SingleCommandInputStringGeneratesSingleElementOutputList()
{
    StringReader reader = new StringReader("a|b" + System.Environment.NewLine);
    InputReader inputReader = new InputReader(reader);

    List<BatchInput> input = inputReader.ReadAllInputs();

    Assert.AreEqual(1, input.Count);
    Assert.AreEqual("a|b", input[0].ToString());
}

[Test]
public void MultipleCommandInputStringGeneratesMultipleElementsInOutputList()
{
    StringReader reader = new StringReader("a|b" + System.Environment.NewLine + "b|c" + Environment.NewLine);
    InputReader inputReader = new InputReader(reader);

    List<BatchInput> input = inputReader.ReadAllInputs();

    Assert.AreEqual(2, input.Count);
    Assert.AreEqual("a|b", input[0].ToString());
    Assert.AreEqual("b|c", input[1].ToString());
}

These tests follow the usual 0, 1, many pattern for implementing functionality. Make sure something works for 0 elements, which fleshes out the API, then make sure it works for a single element, which puts the business logic in, and then make it work for multiple elements, which adds the looping logic. Here is the oh, so complicated code to implement these tests:

public class InputReader : IInputReader
{
    private readonly TextReader reader;

    public InputReader(TextReader reader)
    {
        this.reader = reader;
    }

    public List<BatchInput> ReadAllInputs()
    {
        List<BatchInput> inputData = new List<BatchInput>();
        ReadAllLines().ForEach(delegate(string newLine) 
            { inputData.Add(new BatchInput(newLine)); });
        return inputData;
    }

    private List<string> ReadAllLines()
    {
        List<string> inputLines = new List<string>();
        while (reader.Peek() != -1)
        {
            inputLines.Add(reader.ReadLine());
        }

        return inputLines;
    }
}

And that should pretty well handle the input side of this system.

On to the Processor

The Processor class takes the input BatchInput list and converts it into ProcessOutput objects, which are then written to the output section of the program. Here is the interface again that rules this section of code:

public interface IProcessor
{
    List<ProcessOutput> Process(List<BatchInput> inputs);
}

First of all, let’s make sure that my class is going to implement the correct interface:

[Test]
public void ProcessorImplementsIProcessor()
{
    Assert.IsInstanceOfType(typeof(IProcessor), new Processor(null, null));
}

Now, the responsibilities that seem to have to happen here are that each BatchInput object needs to be turned into something representing a payroll input line, and that new object needs to be executed in some way. Those thought processes lead me to this test:

[Test]
public void SingleBatchInputCausesStuffToHappenOnce()
{
    MockRepository mocks = new MockRepository();

    IPayrollProcessorFactory factory = mocks.CreateMock<IPayrollProcessorFactory>();
    IPayrollExecutor executor = mocks.CreateMock<IPayrollExecutor>();
    Processor processor = new Processor(factory, executor);
    PayrollCommand commonPayrollCommand = new PayrollCommand();

    List<BatchInput> batches = TestDataFactory.CreateBatchInput();

    using (mocks.Record())
    {
        Expect.Call(factory.Create(batches[0])).Return(commonPayrollCommand).Repeat.Once();
        executor.Execute(commonPayrollCommand);
        LastCall.Constraints(Is.Equal(commonPayrollCommand)).Repeat.Once();
    }

    using (mocks.Playback())
    {
        processor.Process(batches);
    }
}

There is tons of stuff to see in this test method now. Immediately after I create my MockRepository, I create my system. This consists of three objects:

  • IPayrollProcessorFactory — responsible for converting the BatchInput object into a PayrollCommand
  • IPayrollExecutor — responsible for executing the PayrollCommand after it is created
  • Processor — the driver of the system

Together, these three classes make up this portion of our system. If I were doing SBT (state-based testing), I’m not entirely sure at all that I would have these two embedded objects yet. I would probably have written code and then refactored to get to where I am now. But, with IBT, you have to think in terms of who the collaborators are that the method under test is going to use, and jump to having those collaborators now rather than later. In fact, the whole IPayrollExecutor seems kind of contrived at this point to me, but I need to have something there to interact with, so I can write an IBT for this.

On the next line, I create an instance of my PayrollCommand. I specifically use this instance as the returned value from the factory.Create call in the first expectation and as a constraint to the executor.Execute in the second expectation. This was something I was struggling with earlier in my experimentation with IBT. What I want to have happen is that I want to force my code to take the object that is returned from the Create call and pass it to the Execute call. By having a common object that I use in the expectations, and having the Is.Equal constraint in the second expectation, I can actually force that to happen. It took me a while to figure this out, and I’m pretty sure that this is a Rhino Mocks thing, rather than a generic IBT thing, but I found this to be helpful.

Then I drop into the record section, where I set some expectations on the objects I’m collaborating with. The first expectation says that I expect an instance of a BatchInput to be provided to this Execute method when called. Please note, and it took me a while to intellectually really grasp this, the batches[0] that I’m passing to the Create method is really just a place holder. This is the weird part here — I’m not actually calling the factory.Create method here, I’m signaling the mocking framework that this is a method I’m about to set some expectations on. I could have just as easily, in this case, passed in null in place of the argument, but I thought null didn’t communicate very clearly. What I do mean is that I expect some instance of a BatchInput to be provided to this method. Maybe I would have done better by new’ing one up in place of using batches[0]???? It is not the value or identity of the object that matters here at all, it is the type, and only because a) the compiler needs it and b) it communicates test intent. The rest of that expectation states that I’m only going to expect this method to be called once, and is allowing me to specify what object will be returned when this method is called. This last part is one of the hardest parts for me to have initially grasped. I was unsure whether this framework was asserting that the mocked method would return the value I passed it, or whether it was allowing me to set up the value that would be returned when it was called. In looking back, the second option is the only one that makes any sense at all, since these expectations are being set on methods that are 100% mocked out and have no ability to return anything without me specifying it in some way. Doh!

The second expectation is where I set up the fact that I expect the same object that was returned from the Create call to be the object passed to this call. Again, I do this through the Constraint, not through the value actually passed to the executor.Execute() method. I could just as easily passed in null there, but it wouldn’t have communicated as clearly.

Finally, I get to the playback section, call my method, and the test is over.

This is the code that I wrote to make this test pass:

public List<ProcessOutput> Process(List<BatchInput> batches)
{
    List<ProcessOutput> results = new List<ProcessOutput>();

    PayrollCommand command = factory.Create(batches[0]);
    executor.Execute(command);

    return results;
}

I know I’m not handling the results at all yet, but I’m pretty sure I can flesh out what will happen with those at some point soon.

In my second test, I’ll worry about how to handle multiple BatchInput objects. Again, this is a very common pattern for me, starting with one of something to get the logic right, and then moving on to multiple, to put in any looping logic I need. Here is the second test:

[Test]
public void MultipleBatchInputsCausesStuffToHappenMultipleTimes()
{
    MockRepository mocks = new MockRepository();

    IPayrollProcessorFactory factory = mocks.CreateMock<IPayrollProcessorFactory>();
    IPayrollExecutor executor = mocks.CreateMock<IPayrollExecutor>();
    Processor processor = new Processor(factory, executor);
    PayrollCommand commonPayrollCommand = new PayrollCommand();

    List<BatchInput> batches = TestDataFactory.CreateMultipleBatches(2);

    using (mocks.Record())
    {
        Expect.Call(factory.Create(batches[0])).
                Constraints(List.OneOf(batches)).Return(commonPayrollCommand).Repeat.Twice();
        executor.Execute(commonPayrollCommand);
        LastCall.Constraints(Is.Equal(commonPayrollCommand)).Repeat.Twice();
    }

    using (mocks.Playback())
    {
        processor.Process(batches);
    }
}

Almost all of this test is exactly the same, except I add two BatchInput objects to my list. The only other thing I need to enforce is that the object that is passed to the factory.Create method is a BatchInput object that is a member of the list I passed in, which I do with the List Constraint to the first expectation.

Here is the modified Processor code:

public List<ProcessOutput> Process(List<BatchInput> batches)
{
    List<ProcessOutput> results = new List<ProcessOutput>();

    foreach (BatchInput batch in batches)
    {
        PayrollCommand command = factory.Create(batch);
        executor.Execute(command);
    }

    return results;
}

Object Mother

In both of these tests, you’ll see a reference to TestDataFactory. This is a class whose responsibility it is to create test data for me when asked. I use it to remove irrelevant details about test data from my tests and move it someplace else. This is called the Object Mother pattern.

In the next episode…

That’s about enough for now. If any of this wasn’t clear, please let me know, and I’ll update the text to be better. In the next episode, I’ll go ahead and build the factory using SBT, since it isn’t going to interact with anything and then dive into the Processor code, which should prove interesting.

Overall, I’m pretty happy with how IBT is allowing me to focus on interactions between objects and ignore details like the contents of my domain classes entirely until I get to a class who manipulates the contents of those domain classes.

My biggest question lies in the area of premature generalization. Am I thinking too much and ignoring YAGNI? Do these tests reflect the simplest thing that can possibly work? I’m truly not sure. I tried to do better in this episode to focus on just payroll stuff and not make generic classes, like the IInputReader. I have a PayrollProcessorFactory, for example, instead of a ProcessorFactory. Those refactorings will come, and I want to wait for the code to tell me about them. IBT, I think, makes it easier to see those abstractions ahead of time, but I need to resist!

Please write with questions and comments. This continues to be an interesting journey for me, and I’m not at all sure where I’m going yet! But it is fun!

— bab

14 thoughts to “Episode 2 – The InputReader and the start of the Processor”

  1. I do wonder about passing null parameter values to the mock objects in the Record section, and then setting your expectations for these parameter values separately. Maybe it’s a Rhino limitation? Maybe Rhino has a more direct way to do this???

    This is how I do it with EasyMock in Java:

    expect(mockObject.someMethod(eq("Parameter Value"))).andReturn("Return Value");

    EasyMock has Matcher creation convenience methods that I can use inline in my "record mode" calls. Is Rhino Mocks missing this feature?

    import static org.easymock.EasyMock.*;

    import static org.junit.Assert.assertEquals;

    import org.junit.Test;

    public class EasyMockParameterTest {

    @Test

    public void testEasyMockParameterChecking() throws Exception {

    final SomeInterface mockObject = createMock(SomeInterface.class);

    expect(mockObject.someMethod(eq("Parameter Value"))).andReturn("Return Value");

    replay(mockObject);

    assertEquals("Return Value", mockObject.someMethod("Parameter Value"));

    verify(mockObject);

    }

    private static interface SomeInterface {

    String someMethod(String someParameter);

    }

    }

  2. OK; I’ve caught up, up to this point, in Java.

    Reactions:

    Wow; that’s a lot of code and classes. And we’ve yet to produce any output.

    Are you /sure/ we’re going to get a working system out of this thing, in the end?!? ;->

  3. ‘IBT isn’t TDD.’

    …at least not in the "classic" sense of TDD.

    See Sean McMillan’s response to my post at

    http://tech.groups.yahoo.com/group/testdrivendevelopment/message/25585

    His YouTube video (sited there) calls for a delicate balance between techniques. …based on what seem to be relatively subtle distinctions between behavior and implementation based tests. To me, that makes it not sound like a traditional Extreme (IE: XP) technique.

  4. I had downloaded the hlaf-life 2 and the instructions tell the next:

    "Create shortcut on hl2.exe and add these parameters: -steam -game ep2

    It will look like: X:somedirportalhl2.exe -steam -game ep2

    Run the shortcut and have fun."

    But in the propieties of the shortcut there are start and destiny (Inicio y Destino, in spanish), but i don’t know how i have to make the instrucctions correctly.

    My direction of destiny is "H:Half Life 2 Episode Twohalf-life 2 episode twohl2.exe" and the start direccion is "H:Half Life 2 Episode Twohalf-life 2 episode two". Can you help me?. Thanks.

Leave a Reply to komik f?kralar Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.