ale*_*ail 31 java unit-testing dependency-injection mockito
假设我有实现它的接口和实现类,我想为此编写单元测试.我应该测试什么接口或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;
}
}
Run Code Online (Sandbox Code Playgroud)
所以,我有HelloInterface和HelloInterfaceImpl来实现它.什么是被测单元接口或Impl?
我认为它应该是HelloInterface.考虑下面的JUnit测试草图:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
主线实际上是我评论过的一条.
((HelloInterfaceImpl)hi).setTarget(target);
方法setTarget()不是我的公共接口的一部分,所以我不想意外地调用它.如果我真的想打电话给我,我应该花点时间考虑一下.例如,它帮助我发现我真正想做的是依赖注入.它为我提供了全新的机会.我可以使用一些现有的依赖注入机制(例如Spring),我可以自己模拟它,就像我在代码中实际做的那样,或采取完全不同的方法.仔细看看,PrintSream的准备工作并不那么容易,也许我应该使用模拟对象代替?
编辑:我想我应该始终专注于界面.从我的观点来看setTarget(),它既不是impl类的"契约"的一部分,也不依赖于依赖注入.我认为从测试的角度来看,Impl类的任何公共方法都应该被认为是私有的.但这并不意味着我忽略了实现细节.
EDIT-2在多个实现\多个接口的情况下,我会测试所有的实现,但是当我在我的setUp()方法中声明一个变量时,我肯定会使用接口.
Tim*_*der 18
实现是需要测试的单元.这当然是您实例化的内容以及包含程序/业务逻辑的内容.
如果你有一个关键接口,并且你想确保每个实现都正确地遵守它,那么你可以编写一个专注于接口的测试套件,并要求传入一个实例(不知道任何实现类型).
是的,将Mockito用于PrintStream可能会更容易,但可能无法像在此特定示例中那样使用模拟对象.
我会测试界面.
我认为错误在于以这样的方式编写实现,即写入System.out很难.你没有办法用另一个PrintStream覆盖你自己.我会使用构造函数而不是setter.这种方式不需要模拟或铸造.
这是一个简单的案例.我想,一个更复杂的工厂会有一个工厂来创建不同的,更复杂的接口实现.希望你不会以这样的方式设计它,以便你被装箱.
坚持测试中的界面也使得模拟变得更加容易.
public class HelloInterfaceImpl implements HelloInterface {
private PrintStream target;
public HelloInterfaceImpl() {
this(System.out);
}
public HelloInterfaceImpl(PrintStream ps) {
this.target = ps;
}
@Override
public void sayHello() {
target.print("Hello World");
}
}
Run Code Online (Sandbox Code Playgroud)
这是测试:
public class HelloInterfaceTest {
@Test
public void testDefaultBehaviourEndsNormally() {
HelloInterface hi = new HelloInterfaceImpl();
hi.sayHello();
}
@Test
public void testCheckHelloWorld() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream target = new PrintStream(out);
HelloInterface hi = new HelloInterfaceImpl(target);
hi.sayHello();
String result = out.toString();
assertEquals("Hello World", result);
}
}
Run Code Online (Sandbox Code Playgroud)
我总是测试实现 - 一个类可以实现几个接口,一个接口可以由几个类实现 - 每个类都应该由测试覆盖.
在单元测试中调用setter的要求(将接口强制转换为实现):
((HelloInterfaceImpl)hi).setTarget(target);
Run Code Online (Sandbox Code Playgroud)
意味着您实际测试实现.这不是合同的一部分,但这是使实施工作的重要部分,应该进行适当的测试.
我们来自JDK的一个例子.你有接口List和两个实现:ArrayList和LinkedList.另外LinkedList实现Deque接口.如果您为List界面编写测试,您将涵盖哪些内容?数组或链表?更重要的是LinkedList,你会选择哪种界面进行测试?Deque还是List?如您所见,当您测试实现时,您没有遇到此类问题.
对我来说,个人来说,在单元测试中实现接口实现是出现问题的明显迹象;)
| 归档时间: |
|
| 查看次数: |
17234 次 |
| 最近记录: |