【问题标题】:Mock is not applied on new object creation模拟不适用于新对象的创建
【发布时间】:2016-05-03 03:53:23
【问题描述】:

我试图模拟一个新对象的创建

public class MyServiceTest {    
    MyClass myClass;
    myClass = Mockito.mock(MyClass.class);
    Mockito.when(new MyClass()).thenReturn(myClass);
}

错误:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

此外,出现此错误的原因可能是: 1.你存根:final/private/equals()/hashCode()方法。 这些方法不能被存根/验证。 不支持在非公共父类上声明的模拟方法。 2. 在 when() 中,你不会在 mock 上调用方法,而是在其他对象上调用。

我确实模拟了 myClass 对象,但它在方法调用时被分配给一个新对象:

public class MyService {
    public static String myMethod(){
    MyClass myClass = new MyClass();
    //..........
    } 
}

【问题讨论】:

    标签: java junit mockito


    【解决方案1】:

    首先:我建议不要使用 PowerMock。因为使用这个框架经常会导致奇怪的错误,并且:你必须明白你面临的不是 Mockito 的“缺陷”......而是你的设计中的缺陷。

    基本上,您想了解如何使用dependency injection。整个想法是:您不要在生产代码中调用 new。因为,你不能嘲笑称“新”。相反,您将生产代码需要的对象推送到您的类中。

    对于正常(生产)工作,您推动正常对象;对于单元测试,你推入模拟对象。

    换句话说:如果您认为您的设计需要对 PowerMock 进行测试;那么这会告诉您您的设计需要重新设计。

    【讨论】:

      【解决方案2】:

      这是正确的行为; Mockito 不支持模拟新对象的创建。您需要另一个库,例如 PowerMock,否则您需要重构您的测试。

      要详细了解基于 Mockito 的测试的每个部分:

      /* 1 */  MyObject myObjectMock = Mockito.mock(MyObject.class);
      /* 2 */  when(myObjectMock.methodCall()).thenReturn("return value");
      /* 3 */  MySystemUnderTest systemUnderTest = new MySystemUnderTest(myObjectMock);
      /* 4 */  systemUnderTest.methodToTest();
      /* 5 */  verify(myObjectMock).methodCalledByTheSystemUnderTest();
      
      1. mock 创建一个模拟对象。请注意,您并没有对 MyObject 的所有实例设置期望;相反,您正在创建一个要控制的实例。在内部,这实际上是 MyObject 的一次性子类,它的所有方法都被覆盖了,所以 Mockito 只对可见的非最终实例方法真正有用(你可以自己覆盖)。

      2. 您可以使用when 调用存根 行为。唯一可以进入when 的是对 Mockito 创建的模拟上的方法的一次调用,所以你的 new 关键字在这里不起作用。

      3. 同样,由于您不能使用new,您通常需要将您的模拟插入到您的测试系统中。你(几乎)从不模拟被测系统;你是在嘲笑合作者,因为你不能使用new,你通常必须把它传递进去。这是why Mockito works so well with dependency injection systems的一部分。

      4. 然后你调用你的被测方法...

      5. ...并检查最终状态是否符合您的要求。这可以是来自测试框架的断言,如 assertEquals,使用 Mockito 创建的模拟调用 verify,或两者的某种组合。

      请记住,仅使用 Mockito,您将无法在调用 new 时让 Java 返回模拟,因此您需要像上面第 3 步一样的步骤。或者,PowerMock 是 EasyMock 或 Mockito 之上的扩展库,它具有像 whenNewmockStatic 这样的静态方法,用于更高级的模拟。 (但要注意:因为 PowerMock 使用特殊的类加载器来重写你的类,它可能更难设置,它的魔力可能会让你的测试更难推理。重构可能是保持测试的更好方法可以理解。)

      【讨论】:

      • 添加了 powermock 并尝试了。 MyClass myClass; myClass = Mockito.mock(MyClass.class); whenNew(MyClass.class).withNoArguments() .thenReturn(myClass ); 但仍然只得到MyService.myMethod() 中的新对象
      • 这只是我;但我永远不会向任何人推荐 PowerMock;尤其不适合新手。相反,应该向人们解释如何进行正确的设计(和 DI)。
      • @Jägermeister 人们对 PowerMockito 是否值得一提非常有意见。在this answer 中,PowerMock 答案以 100% 以上的优势胜过接受的答案。同样,我之前曾试图严厉警告 PowerMock 的字节码操作,它是went over poorly。可以说重构是更好的答案,但忽略 PowerMock 无论如何都是不完整的,否则很可能会在没有解释或警告的情况下添加它。
      • @ep 你已经找到了我所描述的更困难的设置;那里的陷阱值得他们自己的问题,不适合这个评论框。 The wiki goes into more detail,但如前所述,无论如何,重构代码是更好的解决方案。
      【解决方案3】:

      你可以试试 powermock。它对我有用。

      import org.powermock.api.mockito.PowerMockito;
      
      MyClass myClassMock = Mockito.spy(new MyClass());
      PowerMockito.whenNew(MyClass.class).withNoArguments().thenReturn(myClassMock);
      

      【讨论】:

      • 给出错误 java.lang.NoSuchMethodError: org.powermock.reflect.internal.WhiteboxImpl.getUnmockedType(Ljava/lang/Class;)Ljava/lang/Class;
      【解决方案4】:
      public class MyServiceTest {    
          MyClass myClass;
          myClass = PowerMockito.mock(MyClass.class);
          PowerMockito.whenNew(MyClass.class).thenReturn(myClass);
      }
      

      【讨论】:

      • 试试这个!它对我有用。建议:不要尝试在方法中使用 new 运算符创建对象,因为 Spring 框架不建议这样做
      猜你喜欢
      • 1970-01-01
      • 2023-02-26
      • 2017-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多