Нельзя использовать 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
}
}
}