我正在使用EasyMock在我的测试中模拟对象。但是,我如何模拟在我的代码中的其他位置创建的对象?查看以下psudo代码。我想模拟WebService#getPersonById,我该怎么做?
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?
}
}
答案 0 :(得分:9)
如果使用控制和依赖注入的反转来连接服务,模拟通常很有效。所以你的人应该看起来像
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创建模拟和模拟控件,并将其插入到测试中,因为当您创建要测试的Person时,您可以将模拟传递给构造函数(或者如果你去那条路线,那就是设定者)。
在您的真实环境中,IoC容器将注入真实的Web服务。
现在,如果你不想处理所有这些IoC的东西,你需要做的就是将你的web服务与你的Person(应该是PersonService或者其他东西,而不仅仅是Person,它表示实体)分离。换句话说,编写代码的方式只能使用一种类型的WebService。您需要创建它,以便Person只需要某些类型的WebService,而不是您硬编码的特定类型。
最后,在编写的代码中,WebService是一个类,而不是一个接口。 WebService应该是一个接口,您应该进行某种实现。 EasyMock适用于界面;它可能能够模拟具体的类(自从我实际使用它以来已经有一段时间了),但作为一个设计原则,你应该指定所需的接口,而不是具体的类。
答案 1 :(得分:3)
使用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());
}
}
因此,每当我们遇到无法首先编写单元测试的情况时,我们就不能自动断定测试中的代码是不可测试的并且需要重构。有时会出现这种情况,但肯定并非总是如此。也许问题不在于测试中的代码,而在于正在使用的特定模拟工具的局限性。
答案 2 :(得分:1)
我认为你错过了一个更大的问题。测试的难点在于试图告诉你一些事情,即拥有Person
对象(域的一部分)也使用远程服务来查找自身的其他实例(系统的一部分)正在混淆问题。将Person
对象从Person
对象中分离出来,最终会得到更干净,更便携的代码。
不要混淆立即的便利(我手上有一个Person
对象,所以我会用它来获得更多)具有可维护性。
答案 3 :(得分:0)
首先,您需要将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
才能拥有真正的创建方法。
答案 4 :(得分:0)
我最终使用了JMockit。它支持模拟一个类的所有实例。
答案 5 :(得分:0)
您可以尝试使用PowerMock whenNew方法
https://github.com/jayway/powermock/wiki/MockitoUsage#how-to-mock-construction-of-new-objects
此处有更多示例 - http://www.programcreek.com/java-api-examples/index.php?api=org.powermock.api.mockito.PowerMockito
您的代码可能看起来像 -
whenNew(WebService.class).withAnyArguments().thenReturn(yourMockInstanceToWebServiceClass);