Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/368.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何模拟可以模拟的对象';在我的测试中没有实例化?_Java_Mocking_Easymock - Fatal编程技术网

Java 如何模拟可以模拟的对象';在我的测试中没有实例化?

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

我正在使用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?
  }
}

如果您使用控制反转和依赖项注入来连接您的服务,模拟通常工作得很好。所以你的人应该看起来像

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,如果有人在正确答案上击败了另一个人,那还不够。我会从你那里找到一些好的答案,并为你的“体育精神”投票。