思考这个问题的一种方式是:如果我们关心代码的设计,那么EasyMock就是更好的选择,因为它通过其期望的概念向您提供反馈。EasyMock vs Mockito:设计与可维护性?
如果我们关心测试的可维护性(易于阅读,编写,并且不会受到更改影响较小的脆性测试),那么Mockito似乎是更好的选择。
我的问题是:
- 如果您在大型项目中使用EasyMock的,你发现你的测试是难以维持?
- Mockito(Endo测试除外)的局限性是什么?
思考这个问题的一种方式是:如果我们关心代码的设计,那么EasyMock就是更好的选择,因为它通过其期望的概念向您提供反馈。EasyMock vs Mockito:设计与可维护性?
如果我们关心测试的可维护性(易于阅读,编写,并且不会受到更改影响较小的脆性测试),那么Mockito似乎是更好的选择。
我的问题是:
我是一个EasyMock开发者,所以有点偏,但是我已经在大型项目中使用了EasyMock。
我的观点是EasyMock测试确实会在短时间内破坏一次。 EasyMock强迫你做一个你期望的完整记录。这需要一些纪律。你应该记录什么是预期的,而不是测试方法目前需要什么。例如,如果一个方法在模拟上调用了多少时间并不重要,那么不要害怕使用andStubReturn
。另外,如果您不关心参数,请使用anyObject()
等。 TDD的思考可以帮助解决这个问题。
我的分析是,EasyMock测试会更频繁地中断,但Mockito的测试不会在你想要的时候出现。我更喜欢我的测试打破。至少我知道我的发展有什么影响。这当然是我个人的观点。
我不认为你应该太在意这件事。 Easymock和Mockito都可以配置为'strict'或'nice',唯一的区别是默认情况下Easymock是严格的,Mockito很好。
由于所有测试都没有硬性规定,所以您需要平衡测试可信度与可维护性。我通常发现某些功能或技术领域需要高度的信心,我会使用“严格”模拟。例如,我们可能不希望debitAccount()方法被多次调用!然而,在其他情况下,模拟实际上只是一个存根,所以我们可以测试代码的真正“肉”。
在Mockito的生活早期,API兼容性是一个问题,但现在更多的工具支持该框架。 Powermock(我个人的最爱),现在拥有的Mockito扩展
如果我们关心代码的设计则EasyMock的是更好的选择,因为它通过它的预期
有趣的概念给出了反馈给您。我发现“期望概念”使得许多开发人员在测试中提出了更多的期望,只是为了满足意外的方法调用问题。它如何影响设计?
当您更改代码时,测试不应该中断。功能停止工作时,测试应该中断。如果有人喜欢在任何代码更改发生时中断测试,我建议编写一个测试,声明java文件的md5校验和:)
我不会争论这些框架的测试可读性,大小或测试技术,我相信他们是平等的,但在一个简单的例子中,我会告诉你不同之处。
考虑:我们有一类是负责地方存放东西:
public class Service {
public static final String PATH = "path";
public static final String NAME = "name";
public static final String CONTENT = "content";
private FileDao dao;
public void doSomething() {
dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
}
public void setDao(FileDao dao) {
this.dao = dao;
}
}
,我们想测试一下:
的Mockito:
public class ServiceMockitoTest {
private Service service;
@Mock
private FileDao dao;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
// when
service.doSomething();
// then
ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
}
}
了EasyMock:
public class ServiceEasyMockTest {
private Service service;
private FileDao dao;
@Before
public void setUp() {
dao = EasyMock.createNiceMock(FileDao.class);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
Capture<InputStream> captured = new Capture<InputStream>();
dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
replay(dao);
// when
service.doSomething();
// then
assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
verify(dao);
}
}
你可以看到两个测试都是相同的,并且两个测试都通过了。 现在,让我们假设其他人更改了服务实现并尝试运行测试。
新服务实现:
dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));
分离器在路径的终点不断
加入如何测试结果会像现在呢?首先这两个测试会失败,但使用不同的错误信息:
了EasyMock:
java.lang.AssertionError: Nothing captured yet
at org.easymock.Capture.getValue(Capture.java:78)
at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
的Mockito:
Argument(s) are different! Wanted:
dao.store(
"path",
"name",
<Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34)
Actual invocation has different arguments:
dao.store(
"path\",
"name",
[email protected]
);
-> at Service.doSomething(Service.java:13)
在EasyMock的测试发生了什么事,为什么结果还是没捕捉到?商店方法没有被执行,但是等一下,那是为什么EasyMock对我们说谎?
这是因为EasyMock在一行中混合两个职责 - 存根和验证。这就是为什么当出现问题时很难理解哪个部分导致失败。
当然,您可以告诉我 - 只需更改测试并在断言之前移动验证即可。哇,你是否认真,开发者应该记住一些嘲讽框架强制的魔法命令?
顺便说一句,它不会帮助:
java.lang.AssertionError:
Expectation failure on verify:
store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:111)
at org.easymock.classextension.EasyMock.verify(EasyMock.java:211)
不过,这是对我说没有执行的方法,但它是,只能用另一个参数。
为什么Mockito更好?这个框架不会将两个职责混合在一个地方,当你的测试失败时,你会很容易理解为什么。
我首先尝试mockito卖出。测试可维护性已经足够混乱,而模拟框架无法应对。 – Gary 2012-10-26 03:10:41
我意识到这是旧的......但是,你在这里混合验证和存根过程而不是EasyMock。在验证之前做出断言是一种糟糕的做法,因此您可以实际删除'assertThat'调用,因为验证会为您选择:“FileStore.dao(”path“,”name“,capture(Nothing Captured yet)):预期1,实际:0“。 我会承认Mockito的信息在这里更加清晰,但在这个太具体的例子中,这并不足以影响某人对EasyMock的决定! – 2015-12-16 12:34:51
我不知道我是如何在EasyMock中混合一些东西的,这是它的工作方式,所以如果你有一个很好的反例,我会很高兴看到它。我认为这是一个个人观点,不管这个例子是否足够影响这些框架的决策,我很高兴很多人认为它是有价值的。 – 2015-12-16 22:38:38
我更喜欢mockito说实话。一直在使用EasyMock和单元组合,两者的组合通常会导致异常,如IllegalArgumentException:不是接口以及MissingBehaviorExceptions。在这两种情况下,尽管代码和测试代码都非常好。看起来MissingBehaviorException是由于使用createMock创建的模拟对象(使用classextentions !!)确实产生了这个错误。当使用@Mock它确实工作!我不喜欢那种误导性的行为,对我而言,这清楚地表明它的开发者不知道他们在做什么。一个好的框架应该总是易于使用并且不含糊。 IllegalArgumentException也是由于EasyMock内部的混合。另外,录音不是我想要做的。我想测试我的代码是否会抛出异常,并返回预期的结果。这与代码覆盖率相结合对我来说是正确的工具。我不想让我的测试每当我把一行代码放在上一行或下一行时就中断,因为这会提高性能等等。随着mockito这是没有问题的。使用EasyMock,即使代码没有被破坏,也会导致测试失败。那很不好。它花费时间,因此花钱。你想测试预期的行为。你真的关心事物的顺序吗?我想在极少数情况下你可能会。然后使用Easymock。在其他情况下,我认为使用mockito编写测试的时间会少得多。
亲切的问候 劳伦斯
是的,我一直在思考同一个:用的Mockito(和Unitils模拟,类似的嘲讽API)是编写继续愉快地时,他们不应该通过试验容易得多。我怀疑这可能是为什么类似Mockito的应用程序接口(API)过度“松散”测试的主要原因,通常被认为“更容易”。 – 2010-06-08 18:06:54
我有兴趣看到一个对比这两种方法的例子... – Armand 2011-01-28 15:23:53