我们如何测试一个类实现了多个接口?

时间:2016-04-03 10:40:13

标签: java unit-testing testing interface

我的问题是关于测试实现许多接口的类。例如,我有这个类:

public class ServiceControllerImpl extends ServiceController implements IDataChanged, IEventChanged {

}

现在有两种测试方法。第一个是直接测试具体类。这意味着对象类型是具体类而不是接口。

public class ServiceControllerImplTest {
    ServiceControllerImpl instance;
    @Before
     public void setUp() {
         instance = new ServiceControllerImpl();
         // you can bring this instance anywhere
     }
}

第二种方法是仅在界面上进行测试。我们必须将此对象强制转换为它实现的所有接口。

public class ServiceControllerImplTest {
    ServiceController instance;       // use interface here 
    IDataChanged dataChangeListener;

    @Before
     public void setUp() {
         instance = new ServiceControllerImpl();
         dataChangeListener = (IDataChanged) instance;
         // instance and dataChangeListener "look like" two different object.
     }
}

我更喜欢第二种解决方案,因为将来我们可以将它实现的接口更改为其他对象,因此使用具体类可能会导致将来测试失败。我不知道这个问题的最佳实践。

谢谢:)

2 个答案:

答案 0 :(得分:1)

JayC667已经正确地回答了在这些类型定义的方法测试中,通过其超类型引用类是最好的。但是,为了避免投射,我改变了你的方式:

public class ServiceControllerImplTest {
    ServiceController controller;
    IDataChanged dataChangeListener;

    @Before
     public void setUp() {
         instance = new ServiceControllerImpl();
         controller = instance;
         dataChangeListener = instance;
     }
}

答案 1 :(得分:1)

  

我更喜欢第二种解决方案,因为实际上,未来我们可能会将其实现的界面更改为其他对象,因此强制使用混凝土类可能导致将来失败测试。

我想无论如何都会导致测试失败,因为你通常会测试断言是真还是假。问题是:这些测试是否适用于任何IDataChanged或这些断言是否仅适用于ServiceControllerImpl

如果断言仅适用于ServiceControllerImpl,则使用IDataChanged而不是ServiceControllerImpl无关紧要,因为您在使用其他IDataChanged时必须编辑测试1}} object - 不同的断言。如果您使用其他对象,测试将失败。

您设置单元测试的方式本身为您提供答案。单元测试通常单独测试一个类。这意味着您嘲笑环境。但是模拟环境意味着你知道你测试的类的依赖关系,这是实现细节。因此,您的测试是基于实现而不仅仅是接口编写的。

可以编写仅测试抽象API的测试 - 就像接口一样。但这通常意味着您的测试也是抽象的。 E.g。

public abstract class SetTest {

    @Test
    public void addAlreadyExistentObject(){
        Set<String> setUnderTest = createSetUnderTest();
        Assert.assertTrue(setUnderTest.isEmpty());

        boolean setChanged = setUnderTest.add("Hello");
        Assert.assertTrue(setChanged);

        setChanged = setUnderTest.add("Hello");
        Assert.assertFalse(setChanged);

        Assert.assertEquals(setUnderTest.size(), 1);

    }

    protected abstract Set<String> createSetUnderTest();

}

然后,您可以扩展这些抽象测试以测试具体类的api。 E.g。

public class HashSetTest extends SetTest {

    @Override
    protected Set<String> createSetUnderTest() {
        return new HashSet<String>();
    }
}

在这种情况下,您可以替换实现,测试必须保持绿色。

但是这里是另一个抽象api的例子,当替换被测对象时并没有多大意义。 如何为所有Runnable s编写测试?

public class RunnableTest {

     @Test
     public void run(){
         Runnable runnable = ...; 

         // What to test here?
         // run is invoked without throwing any runtime exceptions?
         runnable.run();

     }
}

正如您所看到的那样,在某些情况下以某种方式编写测试是没有意义的,这样您就可以轻松地替换测试中的对象。

如果像Set api这样的api定义了具体的状态处理,你可以编写测试它的抽象测试。