Spock testinde Mock, Stub ve Spy arasındaki farkı anlamıyorum ve çevrimiçi olarak baktığım eğitimler bunları ayrıntılı olarak açıklamıyor.
Spock testinde Mock, Stub ve Spy arasındaki farkı anlamıyorum ve çevrimiçi olarak baktığım eğitimler bunları ayrıntılı olarak açıklamıyor.
Yanıtlar:
Dikkat: Gelecek paragraflarda fazla basitleştireceğim ve hatta belki biraz tahrif edeceğim. Daha ayrıntılı bilgi için Martin Fowler'in web sitesine bakın .
Bir sahte, gerçek olanı değiştiren ve her yöntem çağrısı için boş veya 0 gibi bir değer döndüren sahte bir sınıftır. Aksi takdirde ağ bağlantıları, dosyalar veya veritabanları gibi harici kaynakları kullanacak veya belki düzinelerce başka nesne kullanacak karmaşık bir sınıfın sahte bir örneğine ihtiyacınız varsa bir taklit kullanırsınız. Taklitlerin avantajı, test edilen sınıfı sistemin geri kalanından ayırabilmenizdir.
Saplama aynı zamanda, test edilen belirli taleplere yönelik daha spesifik, hazırlanmış veya önceden kaydedilmiş, tekrar oynatılmış sonuçlar sağlayan sahte bir sınıftır. Saplama, süslü bir taklittir diyebilirsiniz. Spock'ta sık sık saplama yöntemlerini okuyacaksınız.
Casus, gerçek nesne ile saplama arasında bir tür melezdir, yani temelde saplama yöntemleriyle gölgelenen bazı (tümü değil) yöntemlerle gerçek nesnedir. Stubed olmayan yöntemler sadece orijinal nesneye yönlendirilir. Bu şekilde, "ucuz" veya önemsiz yöntemler için orijinal davranışa ve "pahalı" veya karmaşık yöntemler için sahte davranışa sahip olabilirsiniz.
Güncelleme 2017-02-06: Aslında kullanıcının mikhail'in cevabı, yukarıdaki orijinal cevaptan daha Spock'a özgü. Yani Spock kapsamında anlattıkları doğrudur, ancak bu benim genel cevabımı yanlışlamaz:
Şimdi burada neyin mümkün olup neyin olmadığını gösteren çalıştırılabilir bir örnek test var. Mikhail'in snippet'lerinden biraz daha öğretici. Kendi cevabımı geliştirmem için bana ilham verdiği için ona çok teşekkürler! :-)
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?")
}
}
Soru Spock çerçevesi bağlamındaydı ve şu anki yanıtların bunu hesaba kattığına inanmıyorum.
Dayanarak Spock docs (örnekler, kendi ifadeler eklendi özelleştirilmiş):
Stub: Ortak çalışanların yöntem çağrılarına belirli bir şekilde yanıt vermesini sağlamak için kullanılır. Bir yöntemi saplarken, yöntemin kaç kez çağrılacağı umurunuzda değildir; her çağrıldığında bir değer döndürmesini veya bazı yan etkiler gerçekleştirmesini istersiniz.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: Spesifikasyon altındaki nesne ile işbirlikçileri arasındaki etkileşimleri tanımlamak için kullanılır.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
Mock, Mock ve Stub olarak hareket edebilir:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Casus: Her zaman gerçek şeyler yapan orijinal yöntemlerle gerçek bir nesneye dayanır. Seçilen yöntemlerin dönüş değerlerini değiştirmek için bir Stub gibi kullanılabilir. Etkileşimleri açıklamak için bir Mock gibi kullanılabilir.
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
}
Özet:
Stub () yeterliyse Mock () kullanmaktan kaçının.
Yapabiliyorsanız, Spy () kullanmaktan kaçının, bunu yapmak zorunda kalmak bir koku olabilir ve yanlış test veya test edilen nesnenin yanlış tasarımı olabilir.
Basit bir ifadeyle:
Mock: Bir yazı ile alay edersiniz ve anında yaratılan bir nesne elde edersiniz. Bu sahte nesnedeki yöntemler, dönüş türünün varsayılan değerlerini döndürür.
Stub: Yöntemlerin gereksiniminize göre tanımla yeniden tanımlandığı bir saplama sınıfı oluşturursunuz. Ör: Gerçek nesne yönteminde, harici api'yi çağırırsınız ve kullanıcı adını ve kimliğine karşı döndürürsünüz. Stubbed nesne yönteminde bazı sahte adlar döndürürsünüz.
Casus: Bir gerçek nesne yaratırsınız ve sonra onu gözetliyorsunuz. Şimdi bazı yöntemlerle dalga geçebilir ve bazıları için bunu yapmamayı seçebilirsiniz.
Bir kullanım farkı, yöntem düzeyindeki nesnelerle dalga geçememenizdir. oysa yöntemde varsayılan bir nesne oluşturabilir ve ardından casus nesnede yöntemlerin istenen davranışını elde etmek için onu gözetleyebilirsiniz.
Stub'lar gerçekten sadece birim testini kolaylaştırmak içindir, testin bir parçası değildirler. Taklitler, testin, doğrulamanın, geçti / kalmanın bir parçasıdır.
Diyelim ki bir nesneyi parametre olarak alan bir yönteminiz var. Testte bu parametreyi değiştiren hiçbir şeyi asla yapmazsınız. Ondan bir değer okursunuz. Bu bir taslak.
Herhangi bir şeyi değiştirirseniz veya nesneyle bir tür etkileşimi doğrulamanız gerekirse, bu bir sahtedir.