2011-01-26 107 views
9

我使用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

我改用JMockit代替。它支持嘲笑一个类的所有实例。

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测试,你可以将模拟传递给构造函数(或者如果你走这条路线,则使用setter)。

在你的实际环境,IoC容器将注入在真实的Web服务。

现在,如果你不想处理这一切的IoC的东西,你需要做的是从你的脱钩Web服务你的Person(应该调用PersonService或者其他东西,而不仅仅是Person,它表示实体)。换句话说,编写代码的方式只能使用一种类型的WebService。你需要这样做,所以人只需要一些类型的WebService,而不是你已经硬编码的具体的一个。

最后,在编写的代码中,WebService是一个类,而不是一个接口。 WebService应该是一个接口,你应该进行某种实现。 EasyMock与接口配合良好;它可能会嘲笑具体类(自实际使用它之后已经有一段时间了),但作为一个设计原则,您应该指定所需的接口,而不是具体的类。

+0

+1 - 这是几乎一模一样我在我的(现在删除的)答案中添加了什么。 – 2011-01-26 16:58:51

+0

+1好的答案。成为老师的方式。 – jwir3 2011-01-26 17:08:44

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有真正的创建方法。

1

我想你错过了一个更大的问题。测试的难点在于试图告诉你一些事情,即有一个Person对象(域的一部分)也使用远程服务来查找自己的更多实例(系统的一部分)正在混淆关注。从Person对象中分离得到Person,最终得到更清洁,更便携的代码。

不要混淆即时便利(我手里有一个Person对象,所以我会用它来获得更多)的可维护性。

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()); 
    } 
} 

所以,每当我们碰到其中一个单元测试不能在第一次写入的情况下,我们不能自动断定被测试的代码是不可测试的,需要重构。有时候会是这种情况,但肯定不总是如此。也许这个问题不在被测代码中,而在于正在使用的特定模拟工具的局限性。

相关问题