Spock 테스트 프레임 워크에서 Mock / Stub / Spy의 차이점
Spock 테스트에서 Mock, Stub 및 Spy의 차이점을 이해하지 못하며 온라인에서 살펴본 자습서에서 자세히 설명하지 않습니다.
주의 : 나는 다음 단락에서 지나치게 단순화하거나 약간 위조 할 것입니다. 자세한 정보는 Martin Fowler의 웹 사이트를 참조하십시오 .
mock은 실제 클래스를 대체하는 더미 클래스로, 각 메서드 호출에 대해 null 또는 0과 같은 것을 반환합니다. 네트워크 연결, 파일 또는 데이터베이스와 같은 외부 리소스를 사용하거나 수십 개의 다른 개체를 사용하는 복잡한 클래스의 더미 인스턴스가 필요한 경우 모의를 사용합니다. mock의 장점은 테스트중인 클래스를 나머지 시스템과 분리 할 수 있다는 것입니다.
스텁은 또한 테스트중인 특정 요청에 대해 좀 더 구체적이고 준비되거나 미리 녹음 된 재생 결과를 제공하는 더미 클래스입니다. 스텁이 멋진 모의라고 말할 수 있습니다. Spock에서는 스텁 메소드에 대해 자주 읽습니다.
스파이는 실제 객체와 스텁 사이의 일종의 하이브리드입니다. 즉, 기본적으로 일부 (전부는 아님) 메소드가 스텁 메소드에 의해 음영 처리 된 실제 객체입니다. 스텁되지 않은 메서드는 원래 개체로 라우팅됩니다. 이렇게하면 "저렴한"또는 사소한 방법에 대한 원래 동작과 "비싸거나"복잡한 방법에 대한 가짜 동작을 가질 수 있습니다.
업데이트 2017-02-06 : 실제로 사용자 mikhail의 답변은 위의 원래 답변보다 Spock에 더 구체적입니다. 따라서 Spock의 범위 내에서 그가 설명하는 것은 정확하지만 내 일반적인 대답을 위조하지는 않습니다.
- 스텁은 특정 동작을 시뮬레이션하는 것과 관련이 있습니다. Spock에서 이것은 스텁이 할 수있는 모든 것이므로 가장 간단한 것입니다.
- 모의 객체는 (비싼) 실제 객체를 위해 대기하여 모든 메서드 호출에 대해 무 운영 응답을 제공하는 것과 관련이 있습니다. 이와 관련하여 모의는 스텁보다 간단합니다. 그러나 Spock에서 mock은 메서드 결과를 스텁 할 수도 있습니다. 즉, mock과 stub이 될 수 있습니다. 또한 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?")
}
}
질문은 Spock 프레임 워크의 맥락에 있었으며 현재 답변이 이것을 고려한다고 생각하지 않습니다.
Spock 문서를 기반으로합니다 (사용자 정의 된 예제, 내 단어 추가됨) :
Stub: Used to make collaborators respond to method calls in a certain way. When stubbing a method, you don’t care if and how many times the method is going to be called; you just want it to return some value, or perform some side effect, whenever it gets called.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: Used to describe interactions between the object under specification and its collaborators.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
A Mock can act as a Mock and a Stub:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Spy: Is always based on a real object with original methods that do real things. Can be used like a Stub to change return values of select methods. Can be used like a Mock to describe interactions.
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Spy(), used as a Mock an Stub
}
def "should send message to subscriber (actually handle 'receive')"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("message1") // subscriber is a Spy(), used as a Mock, uses real 'receive' function
}
Summary:
- A Stub() is a Stub.
- A Mock() is a Stub and Mock.
- A Spy() is a Stub, Mock and Spy.
Avoid using Mock() if Stub() is sufficient.
Avoid using Spy() if you can, having to do so could be a smell and hints at incorrect test or incorrect design of object under test.
In simple terms:
Mock: You mock a type and on the fly you get an object created. Methods in this mock object returns the default values of return type.
Stub: You create a stub class where methods are redefined with definition as per your requirement. Ex: In real object method you call and external api and return the username against and id. In stubbed object method you return some dummy name.
Spy: You create one real object and then you spy it. Now you can mock some methods and chose not to do so for some.
One usage difference is you can not mock method level objects. whereas you can create a default object in method and then spy on it to get the desired behavior of methods in spied object.
'development' 카테고리의 다른 글
프로그래밍 방식으로 참조를 추가하는 방법 (0) | 2020.10.05 |
---|---|
자동 레이아웃을 사용하여 하위보기가 동적으로 변경된 후 superview 크기 조정 (0) | 2020.10.05 |
Django-외래 키 속성 필터링 (0) | 2020.10.05 |
현재 파비콘을 지원하는 모든 브라우저에서 파비콘을 표시하는 가장 좋은 방법은 무엇입니까? (0) | 2020.10.05 |
SqlBulkCopy에 권장되는 배치 크기는 얼마입니까? (0) | 2020.10.05 |