我正在尝试使用jmocks和junit编写单元测试。 (我的项目使用核心java-没有框架 - )当在一个no arg构造函数中初始化依赖项时,我无法通过模拟外部依赖项来为我的一些类编写单元测试。
由于我无法提供实际代码,试图通过示例解释该方案
public interface Apple {
String variety();
}
实现。
public class MalgovaApple implements Apple {
@Override
public String variety() {
return "Malgova";
}
}
待测试的课程
public class VarietyChecker {
private Apple apple;
VarietyChecker(){
this.apple = new MalgovaApple();
// instead of new, a factory method is used in actual application
}
public String printAppleVariety(){
String variety = apple.variety();
if(variety.length() < 3){
System.out.println("Donot use Code names- Use complete names");
return "bad";
}
return "good";
}
}
使用jmock进行Junit测试
public class VarietyCheckerUnitTest{
Mockery context = new JUnit4Mockery();
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void test_VarietyChecker() throws Exception{
final Apple mockapple = context.mock(Apple.class);
VarietyChecker printer = new VarietyChecker();
context.checking(new Expectations(){{
oneOf(mockapple).variety();will(returnValue("as"));
}});
String varietyNameValid = printer.printAppleVariety();
assertEquals("bad",varietyNameValid);
} }
此测试失败 - 模拟不起作用“未注入”,测试类与MalgovaApple一起执行......
现在,如果我们将下面的构造函数添加到VarietyChecker并使用它测试用例 - 它会给出预期的输出......
public VarietyChecker(Apple apple) {
super();
this.apple = apple;
}
并在单元测试中创建测试类对象 VarietyChecker打印机=新的VarietyChecker(mockapple);
仅仅为了测试目的而公开一个新的构造函数并不是一个好主意。毕竟据说你不应该单独改变测试代码,更重要的是,恐怕我们已经编写了“一些”(金额)代码......
我在junit或jmock中遗漏了一些东西,即使是非arg构造函数也可以使得模拟工作。或者这是简单的junit和jmocks的限制,我应该迁移到强大的东西,如 Jmockit / PowerMock
答案 0 :(得分:2)
您应该考虑两种选择。
在描述时使用构造函数参数。
在这种情况下,你不是“为了测试而暴露一个新的构造函数”。通过允许调用者使用不同的工厂实现,您可以使您的课程更加灵活。
不要嘲笑它。
在这种情况下,您宣称使用其他工厂永远不会有意义。有时候这没关系。不过,那时问题就改变了。而不是,“我如何嘲笑这个?”你现在的问题是,“我从写这个测试中获得了什么?”你可能没有获得太多任何东西,根本不可能编写测试。
如果你没有嘲笑并决定单元测试仍然值得,那么你应该在代码的其他方面断言。结束状态或某些输出。在这种情况下,工厂调用将成为不适合模拟的实现细节。
重要的是不要陷入“单元测试一切”的心态。这是测试引起的设计损坏的一个方法。根据具体情况评估您的测试,确定他们是否为您提供任何实际价值。不写单元测试是一个有效的选项,有时甚至是合适的,即使你很难避免这样做。
在这种情况下,只有你能确定哪一个最有意义。从这个我们正在讨论的工厂对象的事实来看,我可能倾向于前者。