我正在使用Mockito对业务对象进行单元测试。业务对象使用DAO,它通常从数据库获取数据。为了测试业务对象,我意识到使用单独的内存中DAO(将数据保存在HashMap中)比编写所有
更容易when(...).thenReturn(...)
语句。为了创建这样一个DAO,我开始部分模拟我的DAO接口,如下所示:
when(daoMock.getById(anyInt())).then(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
int id = (Integer) invocation.getArguments()[0];
return map.get(id);
}
});
但是我想到,自己实现一个全新的DAO实现(使用内存中的HashMap)更容易,甚至不使用Mockito(不需要从InvocationOnMock对象中获取参数)并生成经过测试的业务对象使用这个新的DAO。
另外,我已经读到部分嘲弄被认为是不好的做法。我的问题是:在我的案例中,我的做法是不是很糟糕?有什么缺点?对我来说这似乎没关系,我想知道潜在的问题是什么。
答案 0 :(得分:2)
我想知道为什么你需要假冒DAO由HashMap
支持。我想知道你的测试是否过于复杂。我非常喜欢拥有非常简单的测试方法,每种方法都测试了SUT行为的一个方面。原则上,这是“每个测试的一个断言”,尽管有时我最终会得到少量实际的assert
或verify
行,例如,如果我断言复杂对象的正确性。请阅读http://blog.astrumfutura.com/2009/02/unit-testing-one-test-one-assertion-why-it-works/或http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html以了解有关此原则的更多信息。
因此,对于每种测试方法,您不应该反复使用假冒DAO。可能只是一次,或最多两次。因此,在我看来,拥有大HashMap
个完整数据似乎是多余的,或者表明您的测试比它应该做的更多。对于每种测试方法,您实际上只需要一两项数据。如果使用DAO接口的Mockito模拟设置它们,并将when ... thenReturn
放在测试方法本身中,则每个测试都将简单易读,因为特定测试使用的数据将立即可见。
您可能还想阅读“排列,行动,断言”模式,(http://www.arrangeactassert.com/why-and-what-is-arrange-act-assert/和http://www.telerik.com/help/justmock/basic-usage-arrange-act-assert.html)并注意在每种测试方法中实施此模式,而不是采用不同的模式它的一部分分散在你的测试类中。
如果没有看到更多的实际测试代码,很难知道其他建议是什么。 Mockito应该让嘲弄更容易,而不是更难;所以,如果你有一个测试没有发生在那里,那么你是否正在做一些非标准的事情当然值得一试。你所做的不是“部分嘲弄”,但它对我来说似乎有点像测试气味。尤其是因为它将大量测试方法结合在一起 - 问问自己如果必须更改HashMap
中的某些数据会发生什么。
答案 1 :(得分:1)
在测试我的课程时,我经常使用Mockito制造的模具和假货的组合,这正是你所描述的。在你的情况下,我同意假的实现听起来更好。
部分模拟并没有什么特别的错误,但是当你调用真实对象以及调用你的模拟方法时,它会让你更难以确定 - 尤其是因为Mockito默默地无法模拟最终方法。对原始类进行无辜的更改可能会更改部分模拟的实现,从而导致测试停止工作。
如果您具有灵活性,我建议您提取一个暴露您需要调用的方法的界面,无论您选择模拟还是伪造,都会更容易。
要写一个假的,使用一个简单的类(如果你愿意,嵌套在你的测试中)实现那个没有Mockito的小接口。这样可以很容易地看到发生了什么;缺点是,如果你写一个非常复杂的假,你可能会发现你也需要测试假。如果你有很多可以使用好的Fake实现的测试,这可能值得额外的代码。
我强烈推荐"Mocks aren't Stubs",一篇由Martin Fowler撰写的文章(因其着作 Refactoring 而闻名)。他回顾了不同类型的测试双打的名称,以及它们之间的差异。