views:

103

answers:

3

So I've started to layout unit tests for the following bit of code:

public interface MyInterface {
  void MyInterfaceMethod1();
  void MyInterfaceMethod2();
}

public class MyImplementation1 implements MyInterface {
  void MyInterfaceMethod1() {
    // do something
  }

  void MyInterfaceMethod2() {
    // do something else
  }

  void SubRoutineP() {
    // other functionality specific to this implementation
  }
}

public class MyImplementation2 implements MyInterface {
  void MyInterfaceMethod1() {
    // do a 3rd thing
  }

  void MyInterfaceMethod2() {
    // do something completely different
  }

  void SubRoutineQ() {
    // other functionality specific to this implementation
  }
}

with several implementations and the expectation of more to come.

My initial thought was to save myself time re-writing unit tests with something like this:

public abstract class MyInterfaceTester {
  protected MyInterface m_object;  

  @Setup
  public void setUp() {
    m_object = getTestedImplementation();
  }

  public abstract MyInterface getTestedImplementation();

  @Test
  public void testMyInterfaceMethod1() {
    // use m_object to run tests
  }

  @Test
  public void testMyInterfaceMethod2() {
    // use m_object to run tests
  }
}

which I could then subclass easily to test the implementation specific additional methods like so:

public class MyImplementation1Tester extends MyInterfaceTester {
  public MyInterface getTestedImplementation() {
    return new MyImplementation1();
  }

  @Test
  public void testSubRoutineP() {
    // use m_object to run tests
  }
}

and likewise for implmentation 2 onwards.

So my question really is: is there any reason not to do this? JUnit seems to like it just fine, and it serves my needs, but I haven't really seen anything like it in any of the unit testing books and examples I've been reading.

Is there some best practice I'm unwittingly violating? Am I setting myself up for heartache down the road? Is there simply a much better way out there I haven't considered?

Thanks for any help.

+11  A: 

is there any reason not to do this?

No. Do it. Tests are classes for exactly this reason.

I haven't really seen anything like it in any of the unit testing books and examples I've been reading.

Keep reading. Introductions don't cover this.

Is there some best practice I'm unwittingly violating?

No.

Am I setting myself up for heartache down the road?

No.

Some folks get nervous about "brittle tests". You can find some questions here looking for ways to make it so a change to the software doesn't also lead to changes to the tests. In the long run, trying to create "robust" tests are silly. You want tests written so that every small change to the visible, interface level of the software requires test rewriting.

You want tests so that invisible, internal changes do not require test rewriting.

Use of classes and subclasses is orthogonal to those considerations.

Is there simply a much better way out there I haven't considered?

No. Object-orientation is the point. Tests are a class for exactly this reason.

S.Lott
+4  A: 

While I support SLott 100% I would also consider JUnit parametrized tests instead of test class hierarchy for this:

@RunWith(Parameterized.class)
public class MyInterfaceTester {
  private MyInterface m_object;  

  public void MyInterfaceTester(MyInterface object) {
    m_object = object;
  }

  @Parameters
  public static Collection<Object[]> data() {
    List<Object[]> list = new ArrayList<Object[]>();

    list.add(new Object[]{new MyImplementation1()});
    list.add(new Object[]{new MyImplementation2()});

    return list;
  }

  @Test
  public void testMyInterfaceMethod1() {
    // use m_object to run tests
  }

  @Test
  public void testMyInterfaceMethod2() {
    // use m_object to run tests
  }
}

No need in test class hierarchy: just add new implementation by adding another list element in data method.

grigory
Didn't know about this feature, thanks for sharing. More info, and a working example here: http://www.devx.com/Java/Article/31983/0/page/3 for anyone else interested.
Tom Tresansky
+1  A: 

If you really are doing the same setup and tear down in each test class, then what you're doing is fine, but I find that in practice this is almost never the case. In fact, most of the time having a setup method which instantiates test data, as you have here, is not even what you want. Instead, within a test class you setup any infrastructure, and each test method sets up its own instance of an object to test some aspect of it.

James Kingsbery
True, moving the getTestedImplementation() call to the first line of each test, and putting the result in a local might be what I want down the line. Probably safer to start with a new object. Right now though, there is no state in the implementation classes, so they should only need to be created once.
Tom Tresansky