I have Spock integration tests for testing my Java app. For one of the methods, the call is going to the real method 'bMethod()', instead of the stubbed valued being returned. It works fine for another method 'aMethd()', and the stubbed value is returned and the actual method is not called. Both these methods are in same class, Calc. Is there a way to investigate what Spock is doing and why?
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
So first I made m1
and m2
return int
instead of 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. 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()
.
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. 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)
}
}
}
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.
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. 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. 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)
Mock(CalcIntf)
→ Mock(Calc)
in 2 places Now the test result is: one passing and one failing feature method:
Condition not satisfied:
math.m2() == 456
| | |
| 6 false
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.