被测单元:Impl 还是 Interface?

假设我有实现它的接口和实现类,我想为此编写单元测试.我应该测试什么接口或Impl?

Suppose I have interface and implementation class that implements it and I want to write unit-test for this. What should I test interface or Impl?

这是一个例子:

public interface HelloInterface {
    public void sayHello();
}


public class HelloInterfaceImpl implements HelloInterface {
    private PrintStream target = System.out;


    @Override
    public void sayHello() {
        target.print("Hello World");

    }

    public void setTarget(PrintStream target){
        this.target = target;
    }
}

所以,我有实现它的 HelloInterface 和 HelloInterfaceImpl.什么是被测单元接口或 Impl?

So, I have HelloInterface and HelloInterfaceImpl that implements it. What is unit-under-test interface or Impl?

我觉得应该是HelloInterface.考虑下面的 JUnit 测试草图:

I think it should be HelloInterface. Consider following sketch of JUnit test:

public class HelloInterfaceTest {
    private HelloInterface hi;

    @Before
    public void setUp() {
        hi = new HelloInterfaceImpl();
    }

    @Test
    public void testDefaultBehaviourEndsNormally() {
        hi.sayHello();
        // no NullPointerException here
    }

    @Test
    public void testCheckHelloWorld() throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream target = new PrintStream(out);
        PrivilegedAccessor.setValue(hi, "target", target);
        //You can use ReflectionTestUtils in place of PrivilegedAccessor
        //really it is DI 
        //((HelloInterfaceImpl)hi).setTarget(target);
        hi.sayHello();
        String result = out.toString();
        assertEquals("Hello World", result);

    }
 }

主线实际上是我注释掉的.

The main line is actually one that I commented out.

((HelloInterfaceImpl)hi).setTarget(target);

方法 setTarget() 不是我的公共接口的一部分,所以我不想不小心 调用它.如果我真的想调用它,我应该花点时间考虑一下.例如,它帮助我发现我真正想做的是依赖注入.它为我打开了整个世界的新机遇.我可以使用一些现有的依赖注入机制(例如 Spring 的),我可以自己模拟它,就像我在代码中实际所做的那样,或者采用完全不同的方法.仔细看,准备 PrintSream 没那么容易,也许我应该改用 mock 对象?

Method setTarget() is not part of my public interface, so I don't want to accidentally call it. If I really want to call it, I should take a moment and think about it. It helps me, for example, to discover that what I'm really trying to do is dependency injection. It opens for me the whole world of new opportunities. I can use some existing dependency injection mechanism (Spring's, for example), I can simulate it myself as I actually did in my code or to take totally different approach. Take a closer look, preparation of PrintSream wasn't that easy, maybe I should use mock object instead?

编辑:我认为我应该始终关注界面.从我的角度来看, setTarget() 也不是 impl 类的合同"的一部分,它为依赖注入服务.我认为从测试的角度来看,任何 Impl 类的公共方法都应该被认为是私有的.但这并不意味着我忽略了实现细节.

EDIT: I think I should always focus on the interface. From my point of view setTarget() is not part of the "contract" of the impl class neither, it serves sally for dependency injection. I think any public method of Impl class should be considered as private from the testing perspective. It doesn't mean that I ignore the implementation details, though.

另请参阅是否应该对私有/受保护方法进行单元测试?

EDIT-2 在多个实现多个接口的情况下,我会测试所有的实现,但是当我在 setUp() 方法中声明一个变量时我肯定会使用界面.

EDIT-2 In the case of multiple implementationsmultiple interfaces, I would test all of the implementations, but when I declare a variable in my setUp() method I would definitely use interface.

推荐答案

实现是需要测试的单元.这当然是您要实例化的内容以及包含程序/业务逻辑的内容.

The implementation is the unit that needs to be tested. That is of course what you are instantiating and what contains the program/business logic.

如果您有一个关键接口,并且希望确保每个实现都正确地遵守它,那么您可以编写一个专注于接口并要求传入实例的测试套件(与任何实现类型无关).

If you had a critical interface and you wanted to make sure every implementation adhered to it properly, then you may write a test suite that focuses on the interface and requires an instance be passed in (agnostic of any implementation type).

是的,将 Mockito 用于 PrintStream 可能会更容易,但可能并不总是可以避免像在此特定示例中那样使用模拟对象.

Yes, it would probably be easier to use Mockito for PrintStream, it may not always be possible to avoid using a mock object like you did in this specific example.

相关文章