Java Easymock部分模拟(Easymock类扩展),好还是坏?

Java Easymock部分模拟(Easymock类扩展),好还是坏?,java,unit-testing,easymock,Java,Unit Testing,Easymock,我已经使用EasyMock编写了很多模拟对象。然而,我经常发现写部分模拟很耗时,而且感觉不“正确” 我认为这是一个设计错误,因为我试图模拟的类有多个关注点,因此我应该创建单独的类来分离关注点 你觉得怎么样?部分嘲弄是好事还是坏事?如果好/坏,为什么?如果你注意到你不能模拟对象,因为你只想模拟几个方法,你会有什么建议?我个人不喜欢部分模拟,因为这意味着你对ClassA的测试在某种程度上取决于ClassB的行为,而模拟的目的是能够测试ClassA独立于任何colloborators的任何实现细节 我

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

我认为这是一个设计错误,因为我试图模拟的类有多个关注点,因此我应该创建单独的类来分离关注点


你觉得怎么样?部分嘲弄是好事还是坏事?如果好/坏,为什么?如果你注意到你不能模拟对象,因为你只想模拟几个方法,你会有什么建议?

我个人不喜欢部分模拟,因为这意味着你对
ClassA
的测试在某种程度上取决于
ClassB
的行为,而模拟的目的是能够测试
ClassA
独立于任何colloborators的任何实现细节

我不明白你所说的“你只想嘲笑几种方法”是什么意思。您正在使用哪个版本的EasyMock?通常,您只需要为实际调用的方法提供期望值和返回值。或者您的意思是您正在编写这些类的存根版本


如果您担心colloborator“将多个关注点合并为一个”,您可以尝试将其接口分解为多个不同的接口,而实现类可以实现所有这些接口。通过这种方式,您可以在单元测试中提供不同的模拟(每个接口一个),即使您的实现仍然只是一个类。

如果您发现自己定期创建部分模拟,这可能是一个迹象,表明有太多的状态和功能被投入到少数类中。这会使代码更难维护和推理,从而更难进行单元测试。如果您稍后发现系统中的其他组件需要一个大型类中包含的功能子集,那么它还可能导致代码重复或循环依赖

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


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

我的意见是:部分模拟是可以的,特别是当您:

•调用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();
}
•模拟
输入流
过程
和其他抽象类:

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

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

如果您想在类A中测试方法X,方法X也会调用类A的方法Y和Z。想想看。我只使用了一次部分模仿,后来就把我想“部分模仿”的所有方法都删掉了。我知道你是说这也是个糟糕的设计,对吗?关于将多个关注点合并为一个,是的,我认为多个接口可能会起作用。但是我有点害怕你生成的大量类。另外,很多东西都是两次编写的,需要重构,听起来像是做了很多额外的工作?创建N个接口只意味着为每个接口定义创建N个新的.java文件。我所指的是将ClassA对ClassB的巨大依赖分解为几个接口,但让ClassBImpl实现所有这些接口。模拟和“向接口编码”实际上有助于减少重构过程中必须进行的更改,因为它清楚地将代码和类分解为模块,这些模块知道如何相互接口,但不知道彼此的所有细节。你是说使用接口来添加特性?比如说,我有一封POJO“电子邮件”。你会对它应用一个接口,使它“序列化为XML”吗?Ie,一个XMLSerializable接口?不。我想我们彼此误解了。非常感谢你的回答(对不起,我还不能打分)。我想你说到点子上了。我发现分离关注点很难,因为我上了这么多的小班。最终,我得到了我拥有的同一个类,但是有一个巨大的构造函数,使用了所有这些小助手类。(也就是说,A类有…X,Y,Z)。这是一种危险,是的。你必须找到一个平衡点。你如何用EasyMock部分地模仿一些东西?这可能吗?您可以使用easymock类扩展。现在我使用Mockito,它可以模拟(部分或全部)对象,而不需要接口。