Java:Spock 强制对未记录的模拟调用进行测试失败
Java: Spock force to fail test on unrecorded mock invocation
我有 java 使用 groovy spock 编写的代码和测试。通常测试遵循这种模式
我的样本Java代码
public User findUser(String id){
return userRepo.findById(id);
}
我的样本测试
def "My Test"(){
given:
String id = "sample"
and:
1 * userRepoMock.findById(id) >> testUser
when:
User user = userServiceUnderTest.findUser(id);
then:
user == testUser
}
其中 and
包含具有调用计数的模拟。
现在想象一下,将来有人向方法添加了另一个调用。喜欢
public User findUser(String id){
anotherRepo.removeTypeById(id);
return userRepo.findById(id);
}
即使有此代码更改,上述测试也将通过而无需任何修改。我如何告诉 spock 在未记录的模拟调用上失败。在这种情况下 anotherRepo.removeTypeById(id);
。我想如果有人添加另一个调用 he/she 强制正确更新测试
IMO,您过度指定了测试。这种测试注定是脆弱的。您不应使用交互测试,除非它对于验证正确的应用程序行为绝对重要。
话虽如此,根据您在问题中提供的少量信息和一些有根据的猜测,我认为您可能想要类似
的内容
0 * anotherRepoMock._(*_)
这是一份完整的 MCVE,这将是您提供的工作。请下次再做,否则我不会再回答,其他人也可能觉得不值得他们花时间。
package de.scrum_master.Whosebug.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.Whosebug.q70029352
class Another {
String id
int amount
}
package de.scrum_master.Whosebug.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.Whosebug.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.Whosebug.q70029352
class UserService {
UserRepository userRepo
AnotherRepository anotherRepo
User findUser(String id){
anotherRepo.removeTypeById(id)
userRepo.findById(id)
}
}
package de.scrum_master.Whosebug.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
}
}
运行 此规范将产生以下错误:
Too many invocations for:
0 * anotherRepoMock._(*_) (1 invocation)
Matching invocations (ordered by last occurrence):
1 * anotherRepoMock.removeTypeById('sample') <-- this triggered the error
我有 java 使用 groovy spock 编写的代码和测试。通常测试遵循这种模式
我的样本Java代码
public User findUser(String id){
return userRepo.findById(id);
}
我的样本测试
def "My Test"(){
given:
String id = "sample"
and:
1 * userRepoMock.findById(id) >> testUser
when:
User user = userServiceUnderTest.findUser(id);
then:
user == testUser
}
其中 and
包含具有调用计数的模拟。
现在想象一下,将来有人向方法添加了另一个调用。喜欢
public User findUser(String id){
anotherRepo.removeTypeById(id);
return userRepo.findById(id);
}
即使有此代码更改,上述测试也将通过而无需任何修改。我如何告诉 spock 在未记录的模拟调用上失败。在这种情况下 anotherRepo.removeTypeById(id);
。我想如果有人添加另一个调用 he/she 强制正确更新测试
IMO,您过度指定了测试。这种测试注定是脆弱的。您不应使用交互测试,除非它对于验证正确的应用程序行为绝对重要。
话虽如此,根据您在问题中提供的少量信息和一些有根据的猜测,我认为您可能想要类似
的内容0 * anotherRepoMock._(*_)
这是一份完整的 MCVE,这将是您提供的工作。请下次再做,否则我不会再回答,其他人也可能觉得不值得他们花时间。
package de.scrum_master.Whosebug.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.Whosebug.q70029352
class Another {
String id
int amount
}
package de.scrum_master.Whosebug.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.Whosebug.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.Whosebug.q70029352
class UserService {
UserRepository userRepo
AnotherRepository anotherRepo
User findUser(String id){
anotherRepo.removeTypeById(id)
userRepo.findById(id)
}
}
package de.scrum_master.Whosebug.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
}
}
运行 此规范将产生以下错误:
Too many invocations for:
0 * anotherRepoMock._(*_) (1 invocation)
Matching invocations (ordered by last occurrence):
1 * anotherRepoMock.removeTypeById('sample') <-- this triggered the error