I have java code and test written using groovy spock. Normally test follows this pattern
My sample Java Code
public User findUser(String id){
return userRepo.findById(id);
}
my sample test
def "My Test"(){
given:
String id = "sample"
and:
1 * userRepoMock.findById(id) >> testUser
when:
User user = userServiceUnderTest.findUser(id);
then:
user == testUser
}
where and
contains mock with invocation count.
Now imagine someone added another invocation to method in future. like
public User findUser(String id){
anotherRepo.removeTypeById(id);
return userRepo.findById(id);
}
even with this code change above test will pass without any modification. How can i tell spock to fail on unrecorded mock invocation. in this case anotherRepo.removeTypeById(id);
. I want if someone add another invocation he/she forced to update the test correctly
IMO, you are overspecifying your test. This kind of test is bound to be brittle. You should not use interaction testing unless it is absolutely vital for verifying the correct application behaviour.
Having said that, with what little information you provide in your question and some educated guesses, I think you might want something like
0 * anotherRepoMock._(*_)
Here is a full MCVE, which would have been your job to provide. Please do that next time, otherwise I am not going to answer again and others might not feel it is worth their time either.
package de.scrum_master.stackoverflow.q70029352
class User {
String id
String name
boolean equals(o) {
if (this.is(o)) return true
if (getClass() != o.class) return false
User user = (User) o
if (id != user.id) return false
if (name != user.name) return false
return true
}
int hashCode() {
int result
result = id.hashCode()
result = 31 * result + name.hashCode()
return result
}
}
package de.scrum_master.stackoverflow.q70029352
class Another {
String id
int amount
}
package de.scrum_master.stackoverflow.q70029352
class UserRepository {
List<User> users = [
new User(id: "a", name: "Alice"),
new User(id: "b", name: "Bob"),
new User(id: "c", name: "Claire")
]
User findById(String id) {
users.findAll { it.id == id }?.first()
}
}
package de.scrum_master.stackoverflow.q70029352
class AnotherRepository {
List<Another> anothers = [
new Another(id: "1", amount: 111),
new Another(id: "2", amount: 222),
new Another(id: "3", amount: 333)
]
void removeTypeById(String id) {}
}
package de.scrum_master.stackoverflow.q70029352
class UserService {
UserRepository userRepo
AnotherRepository anotherRepo
User findUser(String id){
anotherRepo.removeTypeById(id)
userRepo.findById(id)
}
}
package de.scrum_master.stackoverflow.q70029352
import spock.lang.Specification
class UserServiceTest extends Specification {
UserRepository userRepoMock = Mock()
AnotherRepository anotherRepoMock = Mock()
UserService userServiceUnderTest = new UserService(userRepo: userRepoMock, anotherRepo: anotherRepoMock)
User testUser = new User(id: "x", name: "Xander")
def "My Test"() {
given:
String id = "sample"
// Like Leonard said, it would be better to move these interactions
// to the 'then:' block. I am just trying to make your code run
// with minimal changes.
and:
1 * userRepoMock.findById(id) >> testUser
0 * anotherRepoMock._(*_)
when:
User user = userServiceUnderTest.findUser(id);
then:
user == testUser
}
}
Running this specification will yield the following error:
Too many invocations for:
0 * anotherRepoMock._(*_) (1 invocation)
Matching invocations (ordered by last occurrence):
1 * anotherRepoMock.removeTypeById('sample') <-- this triggered the error