单元测试架构问题

发布于 2024-09-04 23:34:43 字数 1656 浏览 4 评论 0原文

因此,我开始为以下代码布局单元测试:

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 似乎很喜欢它,并且它满足了我的需求,但我在我读过的任何单元测试书籍和示例中都没有真正看到过类似的东西。

我是否无意中违反了一些最佳实践?我是否正在为未来的心痛做好准备?难道还有我没有考虑过的更好的出路吗?

感谢您的任何帮助。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧城烟雨 2024-09-11 23:34:43

有什么理由不这样做吗?

不,做吧。测试正是出于这个原因而形成的类。

我在我读过的任何单元测试书籍和示例中都没有真正看到过类似的内容。

继续阅读。介绍不包括这一点。

我是否无意中违反了一些最佳实践?

不。

我是否会让自己在未来的道路上心痛?

不。

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

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

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

是否还有我没有考虑过的更好的出路?

不。面向对象才是重点。正是出于这个原因,测试成为一类。

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.

旧梦荧光笔 2024-09-11 23:34:43

虽然我 100% 支持 SLott 我也会考虑 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 方法中添加另一个列表元素来添加新的实现。

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.

烟雨扶苏 2024-09-11 23:34:43

如果您确实在每个测试类中进行相同的设置和拆卸,那么您所做的就很好,但我发现在实践中几乎从未出现过这种情况。事实上,大多数时候,像这里这样的实例化测试数据的设置方法甚至不是您想要的。相反,在测试类中,您可以设置任何基础结构,并且每个测试方法都设置自己的对象实例来测试它的某些方面。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文