当前位置: 首页 > 知识库问答 >
问题:

捕获spock返回值并将其用于验证

南门刚捷
2023-03-14

如何测试是否调用了具有特定动态值的mocked logService方法?

@SpringBean
private LogService logService = Mock(LogService)

def "test if id is logged"() {
  when:
  createdPersonId = personService.savePerson(personRequest)

  then:
  1 * logService.logSavedId(_)  // it works fine
  1 * logService.logSavedId(createdPersonId) // it doesn't work, createdPersonId is expected to be null instead of real value
}

static class PersonService {
  LogService logService
  PersonRepository personRepository

  int savePerson(PersonRequest personRequest) {
    def id = UUID.randomUUID().toString()
    PersonEntity personEntity = mapRequestToEntity(personRequest)
    entity.id = id

    personRepository.persist(personEntity)

    logService.logSavedId(id)
    return id
  }
}

也许我能抓住人格?

我不想注入UUID提供程序只是为了生成UUID并在测试中模拟它。但是我可以模拟/存根个人存储库(它是由Spring注入的)。

共有1个答案

夏新翰
2023-03-14

您不能在交互中使用createdPersonId,因为then:块中的模拟交互实际上已转换为在时在块之前定义。你有一个自举或母鸡对鸡蛋的问题,看看我的另一个答案。在定义该方法调用中使用的模拟的所需行为和交互时,不能使用测试中方法调用的结果。

但是,您可以这样做(对不起,我不得不推测您在问题中没有显示的依赖类):

package de.scrum_master.stackoverflow

import spock.lang.Specification

class PersonServiceTest extends Specification {
  private LogService logService = Mock(LogService)

  def "test if id is logged"() {
    given:
    def person = new Person(id: 11, name: "John Doe")
    def personRequest = new PersonRequest(person: person)
    def personService = new PersonService(logService: logService)
    def id = personRequest.person.id

    when:
    def createdPersonId = personService.savePerson(personRequest)

    then:
    1 * logService.logSavedId(id)
    createdPersonId == id
  }

  static class Person {
    int id
    String name
  }

  static class PersonRequest {
    Person person
  }

  static class LogService {
    void logSavedId(int id) {
      println "Logged ID = $id"
    }
  }

  static class PersonService {
    LogService logService

    int savePerson(PersonRequest personRequest) {
      def id = personRequest.person.id
      logService.logSavedId(id)
      return id
    }
  }

}

更新:以下是如何重构应用程序以使其更具可测试性的两种变体。

在这里,我们将ID创建分解为一个受保护的方法createId(),然后我们可以在一个名为Spy的部分模拟中进行剔除:

package de.scrum_master.stackoverflow.q60829903

import spock.lang.Specification

class PersonServiceTest extends Specification {
  def logService = Mock(LogService)

  def "test if id is logged"() {
    given:
    def person = new Person(name: "John Doe")
    def personRequest = new PersonRequest(person: person)

    and:
    def personId = "012345-6789-abcdef"
    def personService = Spy(PersonService) {
      createId() >> personId
    }
    personService.logService = logService

    when:
    personService.savePerson(personRequest)

    then:
    1 * logService.logSavedId(personId)
    person.id == personId
  }

  static class Person {
    String id
    String name
  }

  static class PersonRequest {
    Person person
  }

  static class LogService {
    void logSavedId(String id) {
      println "Logged ID = $id"
    }
  }

  static class PersonService {
    LogService logService

    String savePerson(PersonRequest personRequest) {
      def id = createId()
      personRequest.person.id = id
      logService.logSavedId(id)
      return id
    }

    protected String createId() {
      return UUID.randomUUID().toString()
    }
  }

}

在这里,我们将ID创建分解为一个专用类IdCreator,该类易于模拟。它将ID创建与PersonService分离。不需要使用像Spy(PersonService)这样的花哨东西,一个带有注入ID创建者的普通服务实例就足够了。即使在生产使用中,也可以很容易地将UUID创建者重新用于其他对象ID,单独测试它,通过子类覆盖它,或者甚至为了不同的目的重构成具有不同实现的接口。你可能会认为这是过度工程,但我认为不是。解耦和可测试性是软件设计中必须具备的基本要素。

package de.scrum_master.stackoverflow.q60829903

import spock.lang.Specification

class PersonServiceTest extends Specification {
  def logService = Mock(LogService)

  def "test if id is logged"() {
    given:
    def person = new Person(name: "John Doe")
    def personRequest = new PersonRequest(person: person)

    and:
    def personId = "012345-6789-abcdef"
    def idCreator = Stub(IdCreator) {
      createId() >> personId
    }
    def personService = new PersonService(logService, idCreator)

    when:
    personService.savePerson(personRequest)

    then:
    1 * logService.logSavedId(personId)
    person.id == personId
  }

  static class Person {
    String id
    String name
  }

  static class PersonRequest {
    Person person
  }

  static class LogService {
    void logSavedId(String id) {
      println "Logged ID = $id"
    }
  }

  static class IdCreator {
    String createId() {
      return UUID.randomUUID().toString()
    }
  }
  static class PersonService {
    LogService logService
    IdCreator idCreator

    PersonService(LogService logService) {
      this(logService, new IdCreator())
    }

    PersonService(LogService logService, IdCreator idCreator) {
      this.logService = logService
      this.idCreator = idCreator
    }

    String savePerson(PersonRequest personRequest) {
      def id = idCreator.createId()
      personRequest.person.id = id
      logService.logSavedId(id)
      return id
    }
  }

}

 类似资料:
  • 问题内容: 以下代码给出了编译错误,提示“意外运行”: 我知道,如果正常调用函数就可以获取返回值,而无需使用goroutine。或者我可以使用频道等 我的问题是为什么不能从goroutine中获取像这样的返回值。 问题答案: 严格的答案是您 可以 做到。这可能不是一个好主意。下面的代码可以做到这一点: 这将产生一个新的goroutine,它将进行计算,然后将结果分配给。问题是:您将如何使用原始go

  • 我有一个返回< code>List的方法。现在我想知道如何正确放置< code>try/catch块。如果我将< code>return语句放在< code>try中,我会得到错误 并非所有代码路径都返回值 如果我放置在之后(就像我目前所做的那样),即使在之后,它也会返回。那么最好的方法应该是什么? 方法如下:

  • 我正在处理一个Spring Boot java服务,该服务包含一个Camel处理器类,如下所示: 当我运行它时,我得到一个失败,声明对1*LogService.Update(_)的调用太少(0个调用)。我试着调试代码,在MyProc中,语句被命中,并且logService对象在高亮显示时(在Eclipse中)状态为“mock for type logService named$spock_shar

  • 我想要能够让我的catch请求用户输入文件名,直到文件名有效为止,有没有人能给我一个建议,如何安排我的代码做到这一点? 导入java.io.FileNotFoundException;导入java.util.scanner; public class Requierment2{public static void main(String[]args){ }

  • 我正在研究一个使用反射调用另一个方法的方法。然而,“其他方法”可以引发异常,我想用它的原始堆栈信息和内部异常来传播该异常。这只是因为使用反射的方法不应该处理异常,调用方应该处理异常。 下面是代码的简化版本: 该代码显然不会编译,因为Test方法(根据编译器)并不总是返回值。我可以在异常DispatchInfo之后添加一个return false。捕获,但我想知道是否有更好的方法来实现同样的目标。不

  • 本文向大家介绍Python使用迭代器捕获Generator返回值的方法,包括了Python使用迭代器捕获Generator返回值的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python使用迭代器捕获Generator返回值的方法。分享给大家供大家参考,具体如下: 用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须