单元测试架构问题

时间:2010-06-08 16:54:01

标签: java unit-testing junit

所以我开始为以下代码布局单元测试:

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
  }
}

有几个实现,期望有更多的实现。

我最初的想法是节省时间重写单元测试的时间:

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
  }
}

然后我可以轻松地子类化以测试特定于实现的附加方法,如下所示:

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

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

同样适用于推进2以后。

所以我的问题是:有什么理由不这样做吗? JUnit似乎很喜欢它,它满足了我的需求,但我在任何单元测试书和我一直在阅读的例子中都没有看到类似的东西。

是否有一些我无意中违反的最佳做法?我是否会为自己的心痛做好准备?有没有更好的方法,我没有考虑过?

感谢您的帮助。

3 个答案:

答案 0 :(得分:18)

  

有什么理由不这样做吗?

没有。做吧。测试是完全这个原因的类。

  

我在任何单元测试书和我一直在阅读的例子中都没有真正看到过这样的东西。

继续阅读。介绍不包括这个。

  

是否有一些我无意中违反的最佳做法?

没有。

  

我是否为自己的心痛做好准备?

没有。

有些人对“脆弱测试”感到紧张。您可以在此处找到一些问题,寻找实现方法,以便对软件进行更改也不会导致测试更改。从长远来看,尝试创建“强大”测试是愚蠢的。您希望编写测试,以便对软件的可见界面级别进行每次小的更改都需要测试重写。

您需要进行测试,以便不可见的内部更改不需要测试重写。

类和子类的使用与这些考虑正交。

  

我还没有考虑过更好的方法吗?

没有。面向对象的重点。出于这个原因,测试是一个类。

答案 1 :(得分:5)

虽然我支持SLott 100%我还会考虑JUnit参数化测试而不是测试类层次结构:

@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
  }
}

不需要测试类层次结构:只需通过在data方法中添加另一个列表元素来添加新实现。

答案 2 :(得分:1)

如果你真的在每个测试课程中进行相同的设置并拆除,那么你所做的很好,但我发现在实践中几乎从来不是这样。实际上,大多数时候都有一个实例化测试数据的设置方法,就像你在这里一样,甚至不是你想要的。相反,在测试类中设置任何基础结构,每个测试方法都设置自己的对象实例来测试它的某些方面。