【问题标题】:Test if another method was called测试是否调用了另一个方法
【发布时间】:2014-01-16 20:49:37
【问题描述】:

所以我确定那里有类似的东西,但我已经搜索了一个小时,但还没有找到我正在寻找的东西。假设我有一个看起来像这样的类:

public class MyClass
{
    public void myMethod(boolean shouldCallOtherMethod)
    {
        if(shouldCallOtherMethod)
        {
            otherMethod();
        }
    }

    public void otherMethod()
    {
        System.out.println("Called");
    }
}

我该如何做这样的事情?

@Test
public void shouldCallMethod()
{
    MyClass myClass = new MyClass();
    myClass.myMethod(true)

    // verify myClass.otherMethod method was called
}

【问题讨论】:

  • 你的任务一点都不清楚。
  • public boolean myMethod(){} if(myMethod()==true){ //做任务 } else { //做任务 }
  • -1 @PM77-1 评论。 CodesLikeA_Monkey 的任务非常明确:他在标题中直言要测试是否调用了另一个方法。自从他发布问题以来,标题一直相同。

标签: java junit mockito powermock


【解决方案1】:

使用Mockito,您可以像这样监视真实对象

import org.junit.Test;
import static org.mockito.Mockito.*;
public class MyClassTest {
    @Test
    public void otherMethodShouldBeCalled() {
        MyClass myClass = new MyClass();
        MyClass spy = spy(myClass);

        spy.myMethod(true);
        verify(spy).otherMethod();
    }
}

有一些问题,所以也请查看relevant documentation

【讨论】:

    【解决方案2】:

    假设MokeysClass 有一个这样声明的构造函数,其中Foo 是其他一些类。

    public MokeysClass(String name, int counter, Foo myFoo)
    

    我会这样写我的测试。

    @RunWith(MockitoJUnitRunner.class)
    public class TestArray {
        @Mock 
        private Foo mockMyFoo;
        private String nameToInject = "Mokey";
        private int counterToInject = 42;
    
        @Spy 
        private MokeysClass toTest = new MokeysClass(nameToInject, counterToInject, mockMyFoo);
    
        @Test
        public void shouldCallMethod() {
            toTest.myMethod(true);
            verify(toTest).otherMethod();
        }
    }
    

    这样我就明确说明了在创建测试对象时要调用哪个构造函数,以及要传递给它的参数。

    有一些理由不依赖@InjectMocks 为我执行此步骤,特别是如果正在测试的类更复杂并且具有多个构造函数。 Mockito 选择具有最多参数的构造函数,但如果有多个具有相同数量参数的构造函数,则 Mockito 可以选择任何构造函数;也就是说,行为是未定义的。

    一旦 Mockito 选择了一个构造函数,它会检查该构造函数是否实际上可以用于构造函数注入。如果

    • 所选构造函数的一个或多个参数是原始类型,
    • 所选构造函数的一个或多个参数的类型是最终类,
    • 所选构造函数的一个或多个参数的类型是私有类,
    • 类的唯一构造函数是默认构造函数。

    如果其中任何一个条件成立,对于 Mockito 选择的构造函数,则不会使用构造函数注入。在这种情况下,该类必须有一个默认构造函数,否则 Mockito 会抛出异常。

    Mockito 在选择是否应用构造函数注入时使用的标准的复杂性意味着添加或删除构造函数或更改构造函数的参数可以使 Mockito 从使用构造函数注入切换到使用 setter 和字段注入;或者从使用 setter 和字段注入到使用构造函数注入。即使更改的构造函数不是将用于构造函数注入的构造函数,也会发生这种情况。

    因此,任何使用构造函数注入的测试都会自动变得非常脆弱;从某种意义上说,与测试本身不直接相关的更改可能会导致测试失败。此类故障可能难以排除。

    @InjectMocks 注解被设计为与 Spring 等执行依赖注入的框架一起使用;对于使用 Spring 的类的测试,它可能是无价的。但是,如果依赖注入不是您的课程的一部分,我强烈建议您避免使用@InjectMocks,因为它很脆弱。您真的希望您的测试代码与您的生产代码一样易于维护和排除故障。

    【讨论】:

      【解决方案3】:

      这是不推荐,但你可以窥探真实对象:)

      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.mockito.Spy;
      import org.mockito.runners.MockitoJUnitRunner;
      
      import static org.mockito.BDDMockito.verify;
      
      @RunWith(MockitoJUnitRunner.class)
      public class MyClassTest {
      
          @Spy
          private MyClass sut; // System Under Test
      
          @Test
          public void shouldCallMethod() {
      
              // when
              sut.myMethod(true);
      
              // then
              verify(sut).otherMethod();
          }
      }
      

      结果:

      Tests Passed: 1 passed in 0,203 s
      

      改码后:sut.myMethod(false);

      Wanted but not invoked:
      sut.otherMethod();
      -> at my.custom.MyClassTest.shouldCallMethod(MyClassTest.java:23)
      

      来源:Spying on real objects


      带有构造函数注入的魔法版本

      @Mock
      private LexAnalyzer lexAnalyzer;
      
      @Spy
      @InjectMocks
      private SyntaxAnalyzer sut; // System Under Test
      
      @Test
      public void shouldCallMethod() {
      
          // when
          sut.myMethod(true);
      
          // then
          verify(sut).otherMethod();
      }
      

      SyntaxAnalyzer.java

      public class SyntaxAnalyzer {
      
          private final LexAnalyzer lexAnalyzer;
      
          public SyntaxAnalyzer(LexAnalyzer lexAnalyzer) {
              this.lexAnalyzer = lexAnalyzer;
          }
      ...
      

      经过测试,有效;)

      【讨论】:

      • 这里有什么不理想的地方?第二条消息是当测试应该失败! :) 这简直太美了!
      • 对不起,你不可能知道我的意思。我的真实对象有一个构造函数 arg,这使得 @Spy 注释对我不起作用。
      • 没办法,@Spy 喜欢构造函数,给我看看你的代码 :) 只需使用 InjectsMocks
      • 我明白了。这太好了,非常感谢!我希望我可以标记多个答案!
      • 好的,如果我有时间,我稍后会发布一个答案,并解释为什么 @InjectMocks 可能是此类测试要避免的事情。
      【解决方案4】:

      我想你想看看 Mock 对象。您可以创建 MyClass 的模拟,然后设置期望在调用 myMethod 时调用 otherMethod(),如果不调用则失败。

      这里是对它们的一个很好的 Java 概述 - http://www.scalatest.org/user_guide/testing_with_mock_objects

      使用 Mocks 的另一个主要好处是,您可以在测试中避免副作用,例如登录到 NSLog 或访问 Web 服务器或打印)。

      【讨论】:

      • 另外,编写使用模拟对象的 JUnit 测试。
      猜你喜欢
      • 1970-01-01
      • 2015-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-01
      • 1970-01-01
      相关资源
      最近更新 更多