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