Захватите возвращаемое значение спока и используйте его для проверки - PullRequest
0 голосов
/ 24 марта 2020

Как я могу проверить, был ли вызван поддельный метод logService с указанным c dynamici c значением - creatPersonId?

@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
  }
}

Возможно, я мог бы захватить PersonEntity?

Я не знаю не хочу вводить UUID провайдера просто для генерации UUID и проверки его в тесте. Но я могу издеваться / заглушки personRepository (это вводится весной).

1 Ответ

1 голос
/ 24 марта 2020

Нельзя использовать createdPersonId во взаимодействии, потому что фиктивное взаимодействие в блоке then: фактически преобразуется в определение перед блоком when:. У вас проблема с самозагрузкой или курица против яйца, см. мой другой ответ . Вы не можете использовать результат тестируемого вызова метода при определении желаемого поведения и взаимодействий макета, используемого в вызове этого метода.

Вы можете сделать что-то подобное, хотя (извините, я должен размышлять о классы зависимостей, которые вы не показываете в своем вопросе):

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
    }
  }

}

Обновление: Вот два варианта того, как вы можете реорганизовать ваше приложение, чтобы сделать его более тестируемым .

Вариант 1: ввести метод для создания идентификатора

Здесь мы выделяем создание идентификатора в защищенный метод 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()
    }
  }

}

Вариант 2: ввести класс для создания идентификатора

Здесь мы выделяем создание идентификатора в выделенный класс IdCreator, который легко подделать. Он отделяет создание идентификатора от PersonService. Нет необходимости использовать причудливые вещи, такие как Spy(PersonService), достаточно обычного экземпляра службы с создателем введенного идентификатора. Даже в производственном использовании было бы легко повторно использовать создатель UUID для других идентификаторов объектов, проверить его отдельно, переопределить его подклассом или даже преобразовать в интерфейс с различными реализациями для различных целей. Вы можете подумать, что это чрезмерная инженерия, но я думаю, что это не так. Разъединение и тестируемость являются важными вещами в разработке программного обеспечения.

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
    }
  }

}
...