Java 如何模拟可以模拟的对象';在我的测试中没有实例化?
我正在使用EasyMock模拟测试中的对象。但是如何模拟代码中其他地方创建的对象呢?查看以下psudo代码。我想模仿WebService#getPersonById,我该怎么做Java 如何模拟可以模拟的对象';在我的测试中没有实例化?,java,mocking,easymock,Java,Mocking,Easymock,我正在使用EasyMock模拟测试中的对象。但是如何模拟代码中其他地方创建的对象呢?查看以下psudo代码。我想模仿WebService#getPersonById,我该怎么做 public class Person { public Person find(int id) { WebService ws = new WebService(); return ws.getPersonById(id); } } public class PersonTest { te
public class Person {
public Person find(int id) {
WebService ws = new WebService();
return ws.getPersonById(id);
}
}
public class PersonTest {
testFind() {
// How do I mock WebService#getPersonById here?
}
}
如果您使用控制反转和依赖项注入来连接您的服务,模拟通常工作得很好。所以你的人应该看起来像
public class Person() {
WebService ws = null;
// or use setters instead of constructor injection
Persion(WebService ws) {
this.ws = ws;
}
public Person find(int id) {
return ws.getPersonById(id);
}
}
希望通过此更改,您现在可以为WebService创建一个mock和mock控件,并将其插入到测试中,因为当您创建要测试的人员时,您可以将mock传递给构造函数(或者setter,如果您这样做的话)
在您的真实环境中,IoC容器将在中注入真实的web服务
现在,如果您不想处理所有这些IoC内容,那么您需要做的是将您的Web服务与您的Person(应该称为PersonService或其他东西,而不仅仅是Person,它表示实体)分离。换句话说,代码的编写方式只能使用一种类型的Web服务。你需要让这个人只需要某种类型的网络服务,而不是你硬编码的那种
最后,在编写的代码中,WebService是一个类,而不是一个接口。WebService应该是一个接口,您应该加入某种实现。EasyMock可以很好地处理接口;它可能能够模拟具体的类(自从我实际使用它已经有一段时间了),但是作为设计原则,您应该指定所需的接口,而不是具体的类。首先,您需要使
ws
成为模拟,通常是通过注入它
public abstract class Person() {
public Person find(int id) {
WebService ws = createWebService();
return ws.getPersonById(id);
}
protected abstract WebService createWebService();
}
然后您可以将其插入并使用EasyMock.expect设置退货
public class PersonTest() {
testFind() {
WebService mock = EasyMock.createMock(WebService.class);
Person p = new Persion() {
protected WebService createWebService() {
return mock;
}
}
EasyMock.expect(mock.getPersonById()).andReturn(dummyValue);
//Test code
}
}
您还需要一个
PersonImpl
来拥有真正的create方法。我认为您缺少了一个更大的问题。测试的困难在于试图告诉您,拥有一个Person
对象(域的一部分),该对象还使用远程服务来查找自身(系统的一部分)的更多实例,这会混淆问题。将Person
s的获取从Person
对象中分离出来,您将得到更干净、更可移植的代码
不要将即时便利性(我手中有一个
Person
对象,因此我将使用它来获得更多)与可维护性混淆。使用EasyMock(或大多数其他模拟API)无法做到这一点。另一方面,使用JMockit,这样的测试将非常简单和优雅:
public class PersonTest
{
@Test
public testFind(@Mocked final WebService ws) {
final int id = 123;
new NonStrictExpectations() {{
ws.getPersonById(id); result = new Person(id);
}};
Person personFound = new Person().find(id);
assertEquals(id, personFound.getId());
}
}
因此,当我们遇到一个单元测试无法首先编写的情况时,我们不能自动断定被测试的代码是不稳定的,需要重构。有时会是这样,但肯定不总是这样。可能问题不在于测试的代码,而在于所使用的特定模拟工具的局限性。您可以尝试使用PowerMock whenNew方法 这里有更多的例子- 您的代码可能看起来像-
whenNew(WebService.class).withAnyArguments()。然后返回(您的mockInstanceTowBServiceClass)代码>+1-这几乎正是我在(现在已删除)的答案中添加的内容。我想人们可以说,当你无法模拟对象时,会有代码气味?:-)我想我必须重新考虑我的实现。非常感谢你的精彩回答和示例。但是如果我的find方法是静态的呢?@sven,find不应该是静态的。在这种情况下,您将需要Web服务的静态资源。你仍然可以对它设置一个模拟,但它将是uglier@andrzej是的,我注意到你删除了你的帖子。Thanx,如果有人在正确答案上击败了另一个人,那还不够。我会从你那里找到一些好的答案,并为你的“体育精神”投票。