TDDing an Interface - a Twitter influenced discussion 49

Posted by Brett Schuchert Mon, 08 Jun 2009 05:26:00 GMT

Background

I’ve collected a few quick notes regarding TDD and interfaces (as in Java/C# or C++ classes with all pure virtual methods). You dynamic-language-using-types (near irony intentional) can read and laugh to yourselves! You don’t need stinking interfaces, right?

Note, these are based on a few questions from someone I follow on twitter. I considered leaving out the original poster, but since it’s on twitter, I guess that’s not really going to stay hidden. And, quite frankly, his questions are pretty good and quite common.

I’m interested in seeing what other people have to say about this.

Here’s the original question from @GBGames:
When TDDing, I see info on using interfaces, but how do interfaces get created through TDD? How do you unit test your way to an interface?

Interfaces and TDD

There are several reasons an interface comes up while practicing TDD, here are two:
  • You have some existing code that has a concrete dependency on something and you want to remove that relationship.
  • You are working on new production code, taking a mockist approach and you want to create an object, inject dependencies represented by test-doubles that implement interfaces that do not yet exist.

@GBGames goes on to write (remember, this was in Twitter, so I left the abbreviations and such as is):

Game uses HardwareLayer. HL used libSDL but don’t need SDL for unit tests so I wanted IHL for a mockHL. But how to get IHL? Most info I find assumes interface exists or class exists to extract interface. I feel like I’m trying to do 2 things at once.

This additional information suggests something like the following:

@GBGames suggests using an IHardwareLayer to fully get rid of libSDL, which is a fine approach. Another approach is testing the HardwareLayer directly (with unit tests on the HardwareLayer) and passing in an adapter to libSDL (basically an ILibSdl):

What are we Testing?

If the goal is to test responsibility/functionality that belongs in the Game, then creating the IHardwareLayer is the right thing to do. The game only cares about the hardware layer and not the libSDL. If the goal is to test responsibility/functionality that belongs to the Hardware Layer, then that’s where we should start. However, this decision is academic. Why? @GBGames’s original question was, in a nutshell:
When using TDD to test a class that depends on an interface that does not yet exist, how do you go about doing that?
He observed, correctly, that it seems like you are doing two things at the same time, and you are:
  • What methods exist in the interface
  • How will the class under test use that interface.
So how to proceed? Here’s an example I posted a little while back that demonstrates the use of Mockito. My original intent was to demonstrate Mockito. However, that example also shows how, over a series of tests, I:
  • Create interfaces
  • Add methods to those interfaces needed to add functionality
  • Set expectations on the use of those interfaces
There are a few important points from the original question:
  • The Hardware layer, apparently, does not yet exist.
  • By extension, the interface does not yet exist.
  • When writing the real hardware layer class, it will ultimately have to implement all of the methods in the interface created while TDDing the game class
  • Writing the actual hardware layer class will require TDDing it, and passing in an ILibSDL class. This is not strictly necessary, but if you want to test the HardwareLayer without using the real lib SDL, that’s your best option.

Now to get more concrete

So let’s assume that in fact we are starting from scratch on a game and we want to develop it without requiring a real hardware layer. Is it reasonable to assume the use of a hardware layer before we’ve even started writing the game? I say yes. What do you say? So I’d need to come up with some tests (test categories) to get started (I’m just making this up, I don’t have any idea what GBGames is actually working on right now):
  • Creating a game properly initializes the hardware layer
  • Creating a game properly acquires some kind of game configuration information, which is used to configure the hardware layer (e.g., preferred resolution, color depth, anti-aliasing, ...) (Do you see that even these two test categories actually suggest more than just two classes if you consider the single responsibility principle.)

This is enough to start to express my intent. Almost time to start with something.

One final set of questions before I get really started:
  • One assertion per test?
  • TDD or BDD (At the very lest, looks like we’re staring outside in)
  • ...

Test 1: Initializing

How about when I create a game, I want to make sure that it sends some basic initialization to the HardwareLayer:
package com.om.example.gameandhw;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Test;

public class GameTest {
  @Test
  public void itShouldInitializeHardwareLayerDringGameConstruction() {
    IHardwareLayer layer = mock(IHardwareLayer.class);
    new Game(layer);
    verify(layer, times(1)).setResolution(1024, 768);
    verify(layer, times(1)).setColorDepth(8);
  }
}
Note that this is the final test. I had to stop on each of the four lines:
  • On the first line, I had to create and interface IHardwareLayer, no methods.
  • On the first line, I also had to stop to statically import the mock method.
  • On the second line I had to stop and create the Game class.
  • On the second line I had to stop and add a constructor with a parameter (the injected IHardwareLayer, for which I use the Mockito library to create a test-double, which I am using as a Spy)
  • On the third line I had to stop to add the method setResolution to the IHardwareLayer interface.
  • On the third line I had to stop to add the static import of the verify method.
  • On the fourth line I had to stop to add the setColorDepth method to the IHardwareLayer interface.

(Trust me, it too much longer to type that list than to do the work. The actual work was well under 30 seconds to do all of that.)

After this test compiled, but did not pass, I had to write the method for Game. Here are the other two java files I created as a result of this single test (getting the test to pass the first time was included in that 30 seconds above):
package com.om.example.gameandhw;

public interface IHardwareLayer {
  void setResolution(int widthInBits, int heightInBits);
  void setColorDepth(int bitsPerChannel);
}
package com.om.example.gameandhw;

public class Game {
  public Game(IHardwareLayer layer) {
    layer.setResolution(1024, 768);
    layer.setColorDepth(8);
  }
}

Now I have no idea if this is even beginning to make any sense for the original problem. This is where having a pair partner with expertise on the game requirements (and maybe some idea about this libSDL) would greatly help.

Notice, however, that as I try to write the Game class, I am adding methods to the IHardwareLayer class AND figuring out how the game will interact with those methods.

Hope this helps GBGames. I also hope to see some discussion in the comments section.

Brett