【问题标题】:Easymock partially mocking (EasyMock ClassExtension), good or bad?Easymock 部分模拟(EasyMock ClassExtension),好还是坏?
【发布时间】:2009-11-19 13:39:58
【问题描述】:

我已经使用 EasyMock 编写了很多 Mock 对象。但是,我经常发现编写部分模拟很耗时,而且感觉不“正确”。

我会说这是一个设计错误,因为我尝试模拟的类将多个关注点合二为一,因此我应该创建单独的类以分离关注点。

你怎么看?部分嘲笑是好事还是坏事?而且,如果好/坏,为什么?如果您发现无法模拟对象,因为您只想模拟几个方法,您会建议什么?

【问题讨论】:

  • 如何使用 EasyMock 进行部分模拟?这甚至可能吗?
  • 您可以使用 easymock 类扩展。目前我使用 Mockito,它可以模拟(部分或全部)对象,而不需要接口。

标签: java unit-testing easymock


【解决方案1】:

如果您发现自己经常创建部分模拟,这可能表明过多的状态和功能被投入到少数类中。这会使您的代码更难维护和推理,因此更难进行单元测试。如果您稍后发现系统中的某些其他组件需要包含在某个大型类中的功能子集,这也可能导致代码重复或循环依赖。

尝试识别相关的功能组,并将它们分解为可以独立进行单元测试的更小的帮助类。这将使代码更易于理解,使您能够编写更细粒度的单元测试,并且您可能会在未来某个时候找到重用您在不同上下文中拆分的功能的机会。如果您使用的是像 Spring 或 Guice 这样的依赖注入框架,那么当您的应用程序运行时,很容易将这些对象重新连接在一起。

找出重构大型类的最佳方法是从经验中学到的东西。不过,一般来说,我会尝试查看一个类在做什么,并为它在处理过程中的不同点所扮演的不同角色命名。然后我为这些角色创建新类。例如,如果我有一个类读取服务器日志文件并在找到某些条目时向管理员发送电子邮件,我可能会将其重构为一个知道如何解析日志文件的类,第二个类查找触发器条目,第三个知道如何通知管理员。诀窍是限制每个班级包含多少“知识”。这也使您有机会抽象出一般概念。例如,通过这种方式分解您的类,您可以在未来支持不同的通知机制或触发条件类型,而不会影响您的日志解析类或其单元测试。

【讨论】:

  • 非常感谢您的回答(我还不能给分,抱歉)。我想你说得对。我发现分离关注点的困难在于我得到了这么多的小课程。最终,我拥有了与我相同的类,但使用了所有这些小助手类的巨大构造函数。 (即,A 类具有 ... X、Y、Z)。
  • 这是一个危险,是的。你必须取得平衡。
【解决方案2】:

我个人不喜欢部分模拟,因为这意味着您对 ClassA 的测试在某种程度上取决于 ClassB 的行为 - 模拟的重点是能够独立测试 ClassA它的任何合作者的任何实施细节。

我对“您只想模拟几个方法”的意思感到困惑。您使用的是什么版本的 EasyMock?通常,您只需要为实际调用的方法提供期望值和返回值。还是您的意思是您正在编写这些类的存根版本?

如果您担心您的合作者“将多个关注点合二为一”,您总是可以尝试将其接口分解为几个不同的接口 - 实现类可以实现所有这些。这样,即使您的实现仍然只是一个类,您也可以在单元测试中提供不同的模拟(每个接口一个)。

【讨论】:

  • 如果你想在A类中测试方法X。方法X也调用A类的方法Y和Z。来想想吧。我只使用了一次部分模拟,然后将我想要“部分模拟”的所有方法都存根。我看到你说这也是糟糕的设计,对吗?关于将多个关注点合二为一,是的,我认为多个接口可能会奏效。但我有点害怕你生成的大量课程。还有,很多东西都写了两次需要重构,听起来是不是需要做很多额外的工作?
  • 创建 N 个接口只是意味着为每个接口定义创建 N 个新的 .java 文件。我指的是将 ClassA 对 ClassB 的超级依赖分解为几个接口,但只需让 ClassBImpl 实现所有这些接口。
  • 模拟和“接口编码”实际上有助于减少在重构期间必须进行的更改量,因为它清楚地将代码和类分解为知道如何相互接口但不知道如何相互接口的模块彼此的所有细节。
  • 你的意思是使用接口来添加特性?即,假设我有一个 POJO“电子邮件”。您会对其应用一个接口以使其成为“serializeToXML”吗?即,一个 XMLSerializable 接口?
  • 没有。我认为我们彼此误会了。
【解决方案3】:

我的意见是:部分嘲笑是可以的,尤其是当你:

• 调用 JNI 方法的模拟方法,例如

public void methodToTest() {
    int result = invokeLibraryCode();
}

// This method will be mocked
int invokeLibraryCode() {
    // This method is native:
    com.3rdparty.Library.invokeMethod(); 
}

• 使用当前日期操作的模拟方法,而您需要控制日期:

public void methodToTest() {
    Calendar cal = getCurrentDate();
}

// This method will be mocked
Calendar getCurrentDate() {
    return Calendar.getInstance();
}

• 模拟InputStreamProcess 和其他抽象类:

public void methodToTest(InputStream is) throws IOException {
    int i = is.read(); // is.read() is mocked
}

当然,您可以使用接口覆盖前两种情况(将com.3rdparty.Library 包装到您自己的接口中,实现CurrentDateProvider 等,但我认为这过于复杂了)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    相关资源
    最近更新 更多