简体   繁体   English

Spock mock调用mocked class的真实方法

[英]Spock mock calling real method of mocked class

I have Spock integration tests for testing my Java app.我有 Spock 集成测试来测试我的 Java 应用程序。 For one of the methods, the call is going to the real method 'bMethod()', instead of the stubbed valued being returned.对于其中一种方法,调用将转到真正的方法“bMethod()”,而不是返回存根值。 It works fine for another method 'aMethd()', and the stubbed value is returned and the actual method is not called.它适用于另一种方法'aMethd()',并且返回存根值并且不调用实际方法。 Both these methods are in same class, Calc.这两种方法都在相同的 class,Calc。 Is there a way to investigate what Spock is doing and why?有没有办法调查 Spock 在做什么以及为什么?

public interface CalcIntf {
  public int aMethod(int a, int b);      
  public int bMethod(int a, int b, int c);
}

MyTestSpec extends Specification {
    def "aMethod test"() {
        given:
          ...
          CalcIntf calcIntf = Mock(CalcIntf)
          calcIntf.aMethod(_,_) >> { return 100; }
        when:
          //service call
        then:
          // asserts go here
    }

    def "bMethod test"() {
        given:
          ...
          CalcIntf calcIntf = Mock(CalcIntf)
          calcIntf.bMethod(_,_,_) >> { return 100; }
        when:
          // service call
        then:
          // asserts go here
    }
}

Try something like尝试类似的东西

given: 'a math with a calculator that always returns 100'
    def calc = Stub(CalcIntf) {
        bMethod(_,_,_) >> 100
    }
    def math = new Math(calc)

when: 'the math is asked to calculate something'
    def result = math.m2()

then: 'it returns 100'
    result == 100

Points to note:注意事项:

  • return value pushed onto stub should be what is returned rather than a closure推送到存根上的返回值应该是返回的而不是闭包

  • you have to set the stub calc in math by either a constructor or otherwise您必须通过构造函数或其他方式在数学中设置存根计算

  • the methods you are calling on math take no arguments您在数学上调用的方法不采用 arguments

Original code with minimal fixes具有最少修复的原始代码

So first I made m1 and m2 return int instead of void .所以首先我让m1m2返回int而不是void Then at least you can check the results.然后至少你可以检查结果。 Then I also made sure those methods are called correctly without parameters, as Chris already mentioned.然后我还确保在没有参数的情况下正确调用这些方法,正如 Chris 已经提到的那样。 So for now the code looks like this:所以现在代码看起来像这样:

package de.scrum_master.stackoverflow.q62269054

import spock.lang.Specification

class MyTestSpec extends Specification {
  def "testing addition using aMethod"() {
    given:
    Math math = new Math()
    CalcIntf calcIntf = Mock(CalcIntf)
    calcIntf.aMethod(_, _) >> { return 123 }
    expect:
    math.m1() == 123
  }

  def "testing addition using bMethod"() {
    given:
    Math math = new Math()
    CalcIntf calcIntf = Mock(CalcIntf)
    calcIntf.bMethod(_, _, _) >> { return 456 }
    expect:
    math.m2() == 456
  }

  interface CalcIntf {
    int aMethod(int a, int b)

    int bMethod(int a, int b, int c)
  }

  static class Calc implements CalcIntf {
    int aMethod(int a, int b) {
      return a + b
    }

    int bMethod(int a, int b, int c) {
      return a + b + c
    }
  }

  static class Math {
    Calc calc = new Calc()

    int m1() {
      calc.aMethod(1, 2)
    }

    int m2() {
      calc.bMethod(1, 2, 3)
    }
  }
}

Of course, the tests fail:当然,测试失败:

Condition not satisfied:

math.m1() == 123
|    |    |
|    3    false
...

Condition not satisfied:

math.m2() == 456
|    |    |
|    6    false
...

The reason for the failure is that you did not inject your mock anywhere, but Math uses a hard-coded Calc instance via Calc calc = new Calc() .失败的原因是你没有在任何地方注入你的模拟,但是Math通过Calc calc = new Calc()使用了一个硬编码的Calc实例。

Make tests pass使测试通过

So we need a way to inject the mock, eg via constructor or setter.所以我们需要一种注入模拟的方法,例如通过构造函数或设置器。 You also should change the field type from Calc to CalcIntf because otherwise it is impossible to inject an interface mock.您还应该将字段类型从Calc更改为CalcIntf ,否则无法注入接口模拟。 Why create interfaces if you don't use them in your code but code against concrete subclasses?如果您不在代码中使用接口而是针对具体子类编写代码,为什么还要创建接口?

package de.scrum_master.stackoverflow.q62269054

import spock.lang.Specification

class MyTestSpec extends Specification {
  def "testing addition using aMethod"() {
    given:
    CalcIntf calcIntf = Mock(CalcIntf)
    calcIntf.aMethod(_, _) >> { return 123 }
    Math math = new Math(calcIntf)
    expect:
    math.m1() == 123
  }

  def "testing addition using bMethod"() {
    given:
    CalcIntf calcIntf = Mock(CalcIntf)
    calcIntf.bMethod(_, _, _) >> { return 456 }
    Math math = new Math(calcIntf)
    expect:
    math.m2() == 456
  }

  interface CalcIntf {
    int aMethod(int a, int b)

    int bMethod(int a, int b, int c)
  }

  static class Calc implements CalcIntf {
    int aMethod(int a, int b) {
      return a + b
    }

    int bMethod(int a, int b, int c) {
      return a + b + c
    }
  }

  static class Math {
    CalcIntf calcIntf

    Math(CalcIntf calcIntf) {
      this.calcIntf = calcIntf
    }

    int m1() {
      calcIntf.aMethod(1, 2)
    }

    int m2() {
      calcIntf.bMethod(1, 2, 3)
    }
  }
}

Your described test behaviour您描述的测试行为

So now the tests pass, but they do not reproduce the behaviour you described:所以现在测试通过了,但它们不会重现您描述的行为:

For one of the methods, the call is going to the real method 'bMethod()', instead of the stubbed valued being returned.对于其中一种方法,调用将转到真正的方法“bMethod()”,而不是返回存根值。

I can imagine one reason why this would happen: The method you want to stub is final , which means that the dynamic proxy implementing the mock cannot override it and thus not stub it.我可以想象会发生这种情况的一个原因:您要存根的方法是final ,这意味着实现模拟的动态代理不能覆盖它,因此不能存根它。 But this is a completely different case from the one you describe here in your code.但这与您在代码中描述的情况完全不同。

See?看? Your sample code is completely useless if it does not reproduce the problem, which is why in my comment I told you to always provide an MCVE and not some random pseudo code.如果您的示例代码不能重现问题,那么它就完全没用,这就是为什么在我的评论中我告诉您始终提供MCVE而不是一些随机的伪代码。 If somebody reports a bug in your code you also want to know how to reproduce it.如果有人报告了您的代码中的错误,您还想知道如何重现它。

So in order to reproduce a problem like the one you reported, I have to make an educated guess because your code is not helpful.因此,为了重现您报告的问题,我必须做出有根据的猜测,因为您的代码没有帮助。 Just change the following in the previous version:只需在以前的版本中更改以下内容:

  • int bMethod(int a, int b, int c)final int bMethod(int a, int b, int c) int bMethod(int a, int b, int c)final int bMethod(int a, int b, int c)
  • Mock(CalcIntf)Mock(Calc) in 2 places Mock(CalcIntf)Mock(Calc)在 2 个地方

Now the test result is: one passing and one failing feature method:现在测试结果是:一种通过,一种失败的特征方法:

Condition not satisfied:

math.m2() == 456
|    |    |
|    6    false
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM