【问题标题】:How to write unit test by mocking, when you have zero arg:constructors当 arg:constructors 为零时,如何通过模拟编写单元测试
【发布时间】:2015-06-05 21:23:21
【问题描述】:

我正在尝试使用 jmocks 和 junit 编写单元测试。 (我的项目使用核心 java-无框架-)当依赖项在无 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);


} }

此测试失败 - 模拟不起作用,值“as”未注入,测试类使用 MalgovaApple 执行...

现在,如果我们将以下构造函数添加到 VarietyChecker 并使用它测试用例 - 它会给出预期的输出...

public VarietyChecker(Apple apple) {
    super();
    this.apple = apple;
}

并在单元测试中创建测试类对象,如 VarietyChecker 打印机 = new VarietyChecker(mockapple);

仅仅为了测试而公开一个新的构造函数并不是一个好主意。毕竟都说不能单独修改测试代码,不止这些,恐怕我们已经写了“一些”(数量)代码了……

我是否遗漏了 junit 或 jmock 中的某些内容,即使在无参数构造函数的情况下也可以使模拟工作。或者这是简单 junit 和 jmocks 的限制,我应该迁移到像 Jmockit /PowerMock

这样强大的东西吗

【问题讨论】:

  • 如果您必须更改代码才能对其进行测试,那么代码编写得不好。对其他事物的依赖太多,无法正常工作。也就是说,我不能说将批发迁移到 Mockito 会解决您的问题;这是其中之一,我敢肯定。
  • @Makoto 我反对你的第一句话。这是单元测试顽固分子提出的口头禅,不鼓励对测试的价值进行诚实的评估。我以前自己也相信,但那是在我意识到没有银弹之前。现实情况是,编写好的测试并理解它们给你的反馈与编写好的代码一样是一种技能和艺术,而且这种技能也很难掌握。

标签: java unit-testing junit4 jmockit jmock


【解决方案1】:

你应该考虑两个选择。

  1. 按照您的描述使用构造函数参数。

    在这种情况下,您并不是“仅仅为了测试目的而公开一个新的构造函数”。通过允许调用者使用不同的工厂实现,您正在使您的类更加灵活。

  2. 不要嘲笑它。

    在这种情况下,您声明使用不同的工厂是没有意义的。有时这没关系。不过,到那时,问题就变了。而不是“我如何模拟这个?”你现在的问题是,“我从编写这个测试中获得了什么?”您可能不会获得太多任何东西,编写测试可能根本没有多大意义。

    如果您不模拟它并认为单元测试仍然值得,那么您应该在代码的其他方面进行断言。结束状态或某些输出。在这种情况下,工厂调用成为不适合模拟的实现细节。

    重要的是不要陷入“单元测试一切”的心态。这是测试引起的设计损坏的秘诀。根据具体情况评估您的测试,确定它们是否为您提供任何真正的价值。不编写单元测试是一个有效的选择,有时甚至是合适的,即使这是你极力避免的选择。

只有您才能确定在这种情况下哪个最有意义。从这是我们正在谈论的工厂对象这一事实来看,我可能会倾向于前者。

【讨论】:

  • 还有第三种选择(虽然不是很好):在单元测试中使用反射来动态设置VarietyChecker中的apple成员变量
  • @neuronaut 我在发帖时就想到了这种黑客的存在,但想不出更好的方式来呈现它。不过,您启发了一个小修改;我希望你批准。 ;)
  • 我不确定我的例子对真实案例有多清楚 - 实际上我的 VarietyChecker 是 Jersey WebService 类,并且 no: arg 构造函数加载一些属性文件......参数构造函数没有用除了在测试流程中。@jpmc26 你的第二点是一个很好的...我在测试一切模式..但在某些情况下我仍然需要测试
  • @searchingforPerfect 我认为在这种情况下你应该模拟一下。通过允许传入工厂,您可以简化您的课程并增加灵活性。避免将类绑定到特定的配置加载器是有意义的。这是将其余代码与外部系统(文件系统、数据库、外部 Web 服务)隔离开来的好方法。不过,我可能不会对配置加载器本身的内部进行单元测试。
  • @searchingforPerfect 实际上,与其模拟,我可能会在这里提倡 stub。您不是在尝试测试配置加载器是否被正确调用。您正在尝试测试您的类,给定来自配置加载器的某些数据,以后行为是否正确。因此,虽然我将它作为构造函数参数,但我不会断言您传入的工厂对象。(模拟和存根之间的区别更多的是如何区分你使用它们,imo,而不是在实现中。所以你不一定需要新的工具。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-07
相关资源
最近更新 更多