So I am trying to learn Spock and I am struggling with creation of tests.
private WeaponWriter writer;
@Shared
def emptyObject = Mock(Weapon)
@Shared
def weaponMocked = Mock(Weapon)
def: "should flatten object"(){
given:
emptyObject.content() >> ""
weaponMocked.content() >> "BF Sword"
List<Weapon> weaponList = Arrays.asList(weapon1, weapon2)
writer = new WeaponWriter(weaponList)
when:
def text = writer.writeWeapon()
then:
text == expectedText
where:
weapon1 | weapon2 | expectedText
emptyObject | emptyObject | "Headline"
weaponMocked| emptyObject | "Headline" + "BF Sword"
weaponMocked| weaponMocked | "Headline" + "BF Sword" + "BF Sword"
}
As you can see, I want to test class with method which convert list of Weapon
to one string.
And I have three test cases, and only first one is working correctly.
WeaponWriter#writeWeapon
is just iterating over the list of weapons and for each of them calling Weapon#content
and then combines them into one String.
From what I see for some reason mocking of weaponMocked.content() >> "BF Sword"
does not return BF Sword, anyone know why?
When I am not using paramtrized test everything works ok.
Okay, I think I have spotted it: As I said, you use @Shared
mocks instead of creating new ones for each iteration of each feature method, probably because you want to use them from within the where:
block. Then you are trying to stub methods for those mocks from within your test even though shared variables should be initialised either directly during declaration or from a setupSpec()
method. What is even worse, you are trying to re-initialise the stub methods for each iteration, which definitely is a bad idea.
Let us assume we have these classes under test (I am using Groovy, can also be similar Java classes):
package de.scrum_master.stackoverflow.q64013999
class Weapon {
private String name
Weapon(String name) {
this.name = name
}
String content() {
name
}
}
package de.scrum_master.stackoverflow.q64013999
class WeaponWriter {
private List<Weapon> weaponList
WeaponWriter(List<Weapon> weaponList) {
this.weaponList = weaponList
}
String writeWeapon() {
"Headline" + weaponList*.content().join("")
}
}
So now you have at least two choices:
1. Initialise the stubs directly during mock creation:
package de.scrum_master.stackoverflow.q64013999
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class WeaponWriterTest extends Specification {
private WeaponWriter writer
@Shared
def emptyObject = Mock(Weapon) {
content() >> ""
}
@Shared
def weaponMocked = Mock(Weapon) {
content() >> "BF Sword"
}
@Unroll
def "flatten weapon list into '#expectedText'"() {
given:
writer = new WeaponWriter([weapon1, weapon2])
expect:
writer.writeWeapon() == expectedText
where:
weapon1 | weapon2 | expectedText
emptyObject | emptyObject | "Headline"
weaponMocked | emptyObject | "Headline" + "BF Sword"
weaponMocked | weaponMocked | "Headline" + "BF Sword" + "BF Sword"
}
}
2. Initialise the stubs in setupSpec()
:
package de.scrum_master.stackoverflow.q64013999
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class WeaponWriterTest extends Specification {
private WeaponWriter writer
@Shared
def emptyObject = Mock(Weapon)
@Shared
def weaponMocked = Mock(Weapon)
def setupSpec() {
emptyObject.content() >> ""
weaponMocked.content() >> "BF Sword"
}
@Unroll
def "flatten weapon list into '#expectedText'"() {
given:
writer = new WeaponWriter([weapon1, weapon2])
expect:
writer.writeWeapon() == expectedText
where:
weapon1 | weapon2 | expectedText
emptyObject | emptyObject | "Headline"
weaponMocked | emptyObject | "Headline" + "BF Sword"
weaponMocked | weaponMocked | "Headline" + "BF Sword" + "BF Sword"
}
}
Please, please provide an MCVE by yourself next time. Thank you. This was your free shot for me doing your job, asking a proper question.