使用jUnit测试类实现接口作为模板的设计的核心

时间:2018-02-09 22:26:30

标签: java unit-testing junit interface

我有很多简单的类实现一个简单的接口:

public interface MyInterface<T> {
    public T value();
}

当然,我想为所有这些类创建单元测试。假设类MyClass implements MyInterface<String>和以下示例:

public class MyClassTest {

    @Test
    public void valueTest() {
        assertEquals(new MyClass(...).value(), ...);
    }

    @Test
    public void performanceTest() {
        assertTimeout(ofMillis(1), () -> new MyClass(...).value());
    }
}

嗯,这种模式会重复很多次,所以应用模板可以帮助我简化工作。但是,我认为使用接口会更好,因为测试类实际上必须实现所有方法。

public interface MyInterfaceTestTemplate {
    public void valueTest();
    public void performanceTest();
}

在单元测试类中实现它:

public class MyClassTest implements MyInterfaceTestTemplate {

    @Test
    @Override
    public void valueTest() {
        assertEquals(new MyClass(...).value(), ...);
    }

    @Test
    @Override
    public void performanceTest() {
        assertTimeout(ofMillis(1), () -> new MyClass(...).value());
    }
}

我的问题是:

  • 这种方法的优点和缺点是什么?
  • 这种方式是否遵循测试驱动开发的理念?
  • 这是检查单元测试有效性的好方法吗?

2 个答案:

答案 0 :(得分:1)

通常建议不要继承单元测试,但我发现它完全没问题,只要你将基类的继承保持在一个级别和注释上就可以了。

这是我的意思的样本。这可能过于简化,但我只是在编写代码示例:

public class MyClass implements MyInterface<String>{

    @Override
    public String value() {
        return "hi";
    }
}

public abstract class AbstractTest<T> {

    @Test
    public void commonValueTest(){
        assertEquals( createInstance().value(), getValueToCompare() );
    }

    @Test
    public void commonPerformanceTest() {
        assertTimeout( 1000, () -> createInstance().value() );
    }

    protected void assertTimeout(long mills, Runnable e){
        /* add your imple */
    }

    protected abstract MyInterface<T> createInstance();

    protected abstract T getValueToCompare();
}

public class MyClassTest extends AbstractTest<String>{

    @Override
    protected MyInterface<String> createInstance(){
        return new MyClass();
    }

    @Override
    protected String getValueToCompare(){
        return "hi";
    }

    @Test
    public void specificTest() {
        /* your code */
    }
}

答案 1 :(得分:1)

您可以使用Junt5的{em>参数化测试(https://blog.codefx.org/libraries/junit-5-parameterized-tests)代码:

@ParameterizedTest
@MethodSource(names = "createInterfaceImplAndCheckValue")
void testValue(MyInterface impl, Object value) { 
    assertEquals(impl.getValue(),value)
}

private static Stream<Arguments> createInterfaceImplAndCheckValue() {
    return Stream.of(
            ObjectArrayArguments.create(new MyInterfaceImpl1(), "hi"),
            ObjectArrayArguments.create(new MyInterfaceImpl2(), 7));
}

@ParameterizedTest
@MethodSource(names = "createInterfaceImplAndTimeout")
void testPerformance(MyInterface impl, int timeout) { 
        assertTimeout(ofMillis(timeout), () -> impl.value());
}

private static Stream<Arguments> createInterfaceImplAndTimeout() {
    return Stream.of(
            ObjectArrayArguments.create(new MyInterfaceImpl1(), 1),
            ObjectArrayArguments.create(new MyInterfaceImpl2(), 13));
}

原因你也可以将所有内容放在同一个“数据方法”中,只需忽略你不需要的参数:

@ParameterizedTest
@MethodSource(names = "createInterfaceImplAndCheckValue")
void testValue(MyInterface impl, Object value, int timeout) { 
    assertEquals(impl.getValue(),value)
}


@ParameterizedTest
@MethodSource(names = "createInterfaceImplAndCheckValue")
void testPerformance(MyInterface impl, Object value, int timeout) { 
        assertTimeout(ofMillis(timeout), () -> impl.value());
}

private static Stream<Arguments> createInterfaceImplAndCheckValue() {
    return Stream.of(
            ObjectArrayArguments.create(new MyInterfaceImpl1(), "hi", 1),
            ObjectArrayArguments.create(new MyInterfaceImpl2(), 7, 12));
}