9

我正在使用 EasyMock 在我的测试中模拟对象。但是如何模拟在我的代码中其他地方创建的对象?看下面的伪代码。我想模拟 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?
  }
}
4

5 回答 5

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 创建一个 mock 和 mock 控件并将其插入到您的测试中,因为当您创建要测试的 Person 时,您可以将 mock 传递给构造函数(或 setter如果你走那条路)。

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

现在,如果你不想处理所有这些 IoC 的东西,你需要做的是将你的 web 服务与你的 Person 分离(应该调用 PersonService 或其他东西,而不仅仅是表示实体的 Person)。换句话说,代码的编写方式你只能使用一种类型的 WebService。您需要这样做,因此 Person 只需要某种类型的 WebService,而不是您硬编码的特定 WebService。

最后,在编写的代码中,WebService 是一个类,而不是一个接口。WebService 应该是一个接口,并且您应该放入某种实现。EasyMock 与接口配合良好;它可能能够模拟具体类(自从我实际使用它以来已经有一段时间了),但作为设计原则,您应该指定所需的接口,而不是具体类。

于 2011-01-26T16:51:07.597 回答
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());
    }
}

因此,当我们遇到一开始无法编写单元测试的情况时,我们无法自动得出被测代码不可测试且需要重构的结论。有时会是这样,但肯定不会总是如此。也许问题不在于被测代码,而在于正在使用的特定模拟工具的局限性。

于 2011-02-14T13:27:58.523 回答
1

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

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

于 2011-01-27T22:56:34.247 回答
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真正的 create 方法。

于 2011-01-26T16:53:33.610 回答
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);

于 2016-05-07T03:09:23.837 回答