Ответ 1
Внимание: я собираюсь упростить и, возможно, даже слегка фальсифицировать в следующих параграфах. Более подробную информацию см. сайт Мартина Фаулера.
Макет - это фиктивный класс, заменяющий реальный, возвращающий что-то вроде нуля или 0 для каждого вызова метода. Вы используете макет, если вам нужен фиктивный экземпляр сложного класса, который в противном случае использовал бы внешние ресурсы, такие как сетевые подключения, файлы или базы данных или, возможно, использовать десятки других объектов. Преимущество mocks заключается в том, что вы можете изолировать тестируемый класс от остальной системы.
Штук также является фиктивным классом, предоставляющим некоторые более конкретные, подготовленные или предварительно записанные повторно воспроизведенные результаты для определенных тестируемых запросов. Вы могли бы сказать, что заглушка - причудливый макет. В Spock вы часто будете читать о методах заглушки.
Шпион - это своего рода гибрид между реальным объектом и заглушкой, т.е. он в основном является реальным объектом с некоторыми (не всеми) методами, затененными методами заглушки. Необработанные методы просто направляются к исходному объекту. Таким образом, вы можете иметь оригинальное поведение для "дешевых" или тривиальных методов и поддельного поведения для "дорогих" или сложных методов.
Обновление 2017-02-06: На самом деле пользовательский ответ mikhail более специфичен для Spock, чем мой оригинальный выше. Итак, в рамках Спока, то, что он описывает, является правильным, но это не фальсифицирует мой общий ответ:
- Штук касается моделирования конкретного поведения. В Споке это все, что может сделать заглушка, поэтому это своего рода простейшая вещь.
- Макет связан с постоянным (возможно, дорогостоящим) реальным объектом, предоставляющим ответы без ответа для всех вызовов методов. В этом отношении макет проще, чем заглушка. Но в Споке макет может также привести результаты метода заглушки, т.е. Быть как макетом, так и заглушкой. Кроме того, в Spock мы можем подсчитать, как часто во время теста были вызваны определенные макетные методы с определенными параметрами.
- Шпион всегда обертывает реальный объект и по умолчанию направляет все вызовы методов на исходный объект, также проходя через исходные результаты. Метод подсчета вызовов также работает для шпионов. В Spock шпион может также изменять поведение исходного объекта, манипулировать параметрами вызова метода и/или результатами или вообще блокировать исходные методы.
Теперь вот пример исполняемого примера, демонстрирующий, что возможно, а что нет. Это немного более поучительно, чем фрагменты mikhail. Большое спасибо ему за то, что он вдохновил меня улучшить мой собственный ответ!: -)
package de.scrum_master.stackoverflow
import org.spockframework.mock.TooFewInvocationsError
import org.spockframework.runtime.InvalidSpecException
import spock.lang.FailsWith
import spock.lang.Specification
class MockStubSpyTest extends Specification {
static class Publisher {
List<Subscriber> subscribers = new ArrayList<>()
void addSubscriber(Subscriber subscriber) {
subscribers.add(subscriber)
}
void send(String message) {
for (Subscriber subscriber : subscribers)
subscriber.receive(message);
}
}
static interface Subscriber {
String receive(String message)
}
static class MySubscriber implements Subscriber {
@Override
String receive(String message) {
if (message ==~ /[A-Za-z ]+/)
return "ok"
return "uh-oh"
}
}
Subscriber realSubscriber1 = new MySubscriber()
Subscriber realSubscriber2 = new MySubscriber()
Publisher publisher = new Publisher(subscribers: [realSubscriber1, realSubscriber2])
def "Real objects can be tested normally"() {
expect:
realSubscriber1.receive("Hello subscribers") == "ok"
realSubscriber1.receive("Anyone there?") == "uh-oh"
}
@FailsWith(TooFewInvocationsError)
def "Real objects cannot have interactions"() {
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then:
2 * realSubscriber1.receive(_)
}
def "Stubs can simulate behaviour"() {
given:
def stubSubscriber = Stub(Subscriber) {
receive(_) >>> ["hey", "ho"]
}
expect:
stubSubscriber.receive("Hello subscribers") == "hey"
stubSubscriber.receive("Anyone there?") == "ho"
stubSubscriber.receive("What else?") == "ho"
}
@FailsWith(InvalidSpecException)
def "Stubs cannot have interactions"() {
given: "stubbed subscriber registered with publisher"
def stubSubscriber = Stub(Subscriber) {
receive(_) >> "hey"
}
publisher.addSubscriber(stubSubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then:
2 * stubSubscriber.receive(_)
}
def "Mocks can simulate behaviour and have interactions"() {
given:
def mockSubscriber = Mock(Subscriber) {
3 * receive(_) >>> ["hey", "ho"]
}
publisher.addSubscriber(mockSubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then: "check interactions"
1 * mockSubscriber.receive("Hello subscribers")
1 * mockSubscriber.receive("Anyone there?")
and: "check behaviour exactly 3 times"
mockSubscriber.receive("foo") == "hey"
mockSubscriber.receive("bar") == "ho"
mockSubscriber.receive("zot") == "ho"
}
def "Spies can have interactions"() {
given:
def spySubscriber = Spy(MySubscriber)
publisher.addSubscriber(spySubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then: "check interactions"
1 * spySubscriber.receive("Hello subscribers")
1 * spySubscriber.receive("Anyone there?")
and: "check behaviour for real object (a spy is not a mock!)"
spySubscriber.receive("Hello subscribers") == "ok"
spySubscriber.receive("Anyone there?") == "uh-oh"
}
def "Spies can modify behaviour and have interactions"() {
given:
def spyPublisher = Spy(Publisher) {
send(_) >> { String message -> callRealMethodWithArgs("#" + message) }
}
def mockSubscriber = Mock(MySubscriber)
spyPublisher.addSubscriber(mockSubscriber)
when:
spyPublisher.send("Hello subscribers")
spyPublisher.send("Anyone there?")
then: "check interactions"
1 * mockSubscriber.receive("#Hello subscribers")
1 * mockSubscriber.receive("#Anyone there?")
}
}