【问题标题】:Java - contract testsJava - 合约测试
【发布时间】:2012-06-22 13:23:11
【问题描述】:

我正在尝试为一些广泛使用的接口编写契约测试:

大致如下:

public abstract class MyInterfaceContractTest extends TestCase {

  private MyInterface _toTest;

  public void setUp(){
    _toTest = getTestableImplementation();
  }

  protected abstract MyInterface getTestableImplementation();

  public void testContract(){

  }
}

...和...

public class MyInterfaceImplementationTest extends MyInterfaceContractTest {

  protected MyInterface getTestableImplementation(){
    return new MyInterfaceImplementation(...);
  }
}

但是,我希望能够测试MyInterfaceImplementation 的多个实例。在我的用例中,这是一个包含数据集合的不可变对象(根据接口MyInterface 指定访问器),它可能是空的,或者有少量数据,甚至是大量数据。

所以问题是,我如何测试我的实现的多个实例?

目前,我必须初始化实现以将其传递给抽象合约测试。一种方法是为每个实现设置多个测试类,其中每个测试类测试该实现的一个特定实例 - 但这似乎有点庞大且难以跟踪。

FWIW,我正在使用 JUnit 3。

【问题讨论】:

  • JUnit 3 是古老的。为什么不使用 JUnit 4?
  • @DonRoby - 这对解决我的问题会有影响吗?我正在使用 JUnit 3,因为这是我们当前编写的单元测试的内容,升级到 JUnit 4 从来都不是优先事项。
  • 如果我理解正确,您可以根据要求使用工厂来创建您的测试实施实例。我认为还有其他一些方法可以解决这个问题。

标签: java unit-testing interface junit


【解决方案1】:

通常,该方法是使用抽象类的“可测试”子类在一次测试中测试抽象类的所有功能。然后为每个具体实现编写单独的测试,仅测试具体类中定义/实现的方法(不要重新测试具体类中的功能)。

【讨论】:

  • +1:对于其中定义了一些功能的抽象类来说,这绝对是一个很好的策略。对于普通接口的多个实现,您可能最好使用参数化测试。
  • 我这样做是为了测试实现,也没有额外的方法。失败消息比参数化测试更好,并且更容易有选择地为单个实现运行测试。
【解决方案2】:

如果我正确理解了您的需求,您希望运行相同的测试方法或具有相同接口的多个实现的方法。

我不知道如何在 JUnit 3 中很好地做到这一点。

如果您愿意升级到 JUnit 4,这可以通过使用参数化测试来完成。

对于在接口的两个实现上运行单个测试方法的简单示例,您的测试代码可能如下所示:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;

import static junit.framework.Assert.assertEquals;

// Tell JUnit4 to run it as a parametrized test.
@RunWith(Parameterized.class)
public class MyInterfaceContractTest {

    private MyInterface _toTest;

    // Load the test data into your test instance via constructor
    public MyInterfaceContractTest(MyInterface impl) {
        this._toTest = impl;
    }

    // Supply the test data, in this example just the two implementations you want to test.
    @Parameterized.Parameters
    public static Collection<Object[]> generateData() {
        return Arrays.asList(new Object[]{new MyInterfaceImpl1()}, new Object[]{new MyInterfaceImpl2()});
    }

    @Test
    public void testContract(){
        // assert whatever, using your _toTest field
    }
}

在运行这个测试时,JUnit 将运行两次测试,使用参数列表中的连续条目调用构造函数。

如果您有更复杂的事情,例如对不同实现的不同期望,数据生成可能会返回包含多个元素的 Object 数组列表,然后构造函数将采用相应的多个参数。

如果您需要在测试方法之间重新初始化被测对象,您可能还想使用我在this related question 中描述的技巧。

我相信在TestNG 中可能会发生类似的事情,这可能是您升级路径的另一个选择。

【讨论】:

  • 超级 - 谢谢。我花了几天时间才决定改用 Junit 4,但这是一个不错的解决方案。
猜你喜欢
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 2021-10-25
  • 2018-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-08
相关资源
最近更新 更多