Tôi không hiểu sự khác biệt giữa Mock, Stub và Spy trong thử nghiệm Spock và các hướng dẫn tôi đã xem trên mạng không giải thích chi tiết về chúng.
Tôi không hiểu sự khác biệt giữa Mock, Stub và Spy trong thử nghiệm Spock và các hướng dẫn tôi đã xem trên mạng không giải thích chi tiết về chúng.
Câu trả lời:
Chú ý: Tôi sẽ đơn giản hóa quá mức và thậm chí có thể hơi sai lệch trong các đoạn văn sắp tới. Để biết thêm thông tin chi tiết, hãy xem trang web của Martin Fowler .
Mô hình là một lớp giả thay thế một lớp thực, trả về một thứ gì đó như null hoặc 0 cho mỗi lần gọi phương thức. Bạn sử dụng một mô hình nếu bạn cần một phiên bản giả của một lớp phức tạp mà nếu không sẽ sử dụng các tài nguyên bên ngoài như kết nối mạng, tệp hoặc cơ sở dữ liệu hoặc có thể sử dụng hàng chục đối tượng khác. Ưu điểm của mocks là bạn có thể cách ly lớp đang kiểm tra với phần còn lại của hệ thống.
Sơ khai cũng là một lớp giả cung cấp một số kết quả cụ thể hơn, được chuẩn bị hoặc ghi trước, phát lại cho các yêu cầu nhất định đang được thử nghiệm. Bạn có thể nói sơ khai là một sự giả tạo ưa thích. Trong Spock, bạn thường sẽ đọc về các phương thức sơ khai.
Một spy là loại lai giữa đối tượng thực và sơ khai, tức là về cơ bản nó là đối tượng thực với một số (không phải tất cả) phương thức bị che bởi các phương thức sơ khai. Các phương thức không có gốc chỉ được chuyển đến đối tượng gốc. Bằng cách này, bạn có thể có hành vi ban đầu cho các phương thức "rẻ" hoặc tầm thường và hành vi giả mạo cho các phương pháp "đắt" hoặc phức tạp.
Cập nhật 2017-02-06: Thực tế câu trả lời của người dùng mikhail cụ thể hơn cho Spock so với câu trả lời ban đầu của tôi ở trên. Vì vậy, trong phạm vi của Spock, những gì anh ấy mô tả là đúng, nhưng điều đó không làm sai lệch câu trả lời chung của tôi:
Bây giờ đây là một thử nghiệm ví dụ có thể thực thi, chứng minh điều gì có thể và điều gì không. Nó mang tính hướng dẫn nhiều hơn một chút so với các đoạn trích của mikhail. Rất cám ơn anh ấy đã truyền cảm hứng để tôi cải thiện câu trả lời của chính mình! :-)
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?")
}
}
Câu hỏi đặt ra trong bối cảnh của khung Spock và tôi không tin rằng các câu trả lời hiện tại có tính đến điều này.
Dựa trên tài liệu Spock (ví dụ được tùy chỉnh, từ ngữ của riêng tôi được thêm vào):
Stub: Được sử dụng để làm cho các cộng tác viên trả lời các cuộc gọi phương thức theo một cách nhất định. Khi khai báo một phương thức, bạn không quan tâm phương thức đó sẽ được gọi bao nhiêu lần; bạn chỉ muốn nó trả về một số giá trị hoặc thực hiện một số hiệu ứng phụ, bất cứ khi nào nó được gọi.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: Được sử dụng để mô tả các tương tác giữa đối tượng được đặc tả và các cộng tác viên của nó.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
Mock có thể hoạt động như một Mock và Stub:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Spy: Luôn dựa trên một đối tượng thực với các phương pháp ban đầu làm những việc thực. Có thể được sử dụng như một Stub để thay đổi giá trị trả về của các phương thức được chọn. Có thể được sử dụng như Mock để mô tả các tương tác.
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
}
Tóm lược:
Tránh sử dụng Mock () nếu Stub () là đủ.
Tránh sử dụng Spy () nếu bạn có thể, việc làm như vậy có thể là một mùi và gợi ý về việc kiểm tra không chính xác hoặc thiết kế không chính xác của đối tượng được kiểm tra.
Nói một cách dễ hiểu:
Mock: Bạn mô phỏng một kiểu và ngay lập tức bạn sẽ tạo được một đối tượng. Các phương thức trong đối tượng giả lập này trả về các giá trị mặc định của kiểu trả về.
Stub: Bạn tạo một lớp sơ khai nơi các phương thức được định nghĩa lại với định nghĩa theo yêu cầu của bạn. Ví dụ: Trong phương thức đối tượng thực, bạn gọi và api bên ngoài và trả về tên người dùng đối với và id. Trong phương thức đối tượng gốc, bạn trả về một số tên giả.
Gián điệp: Bạn tạo ra một đối tượng thực và sau đó bạn theo dõi nó. Bây giờ bạn có thể bắt chước một số phương pháp và chọn không làm như vậy đối với một số phương pháp.
Một sự khác biệt khi sử dụng là bạn không thể giả lập các đối tượng mức phương thức. trong khi bạn có thể tạo một đối tượng mặc định trong phương thức và sau đó theo dõi nó để có được hành vi mong muốn của các phương thức trong đối tượng gián điệp.
Stubs thực sự chỉ để tạo điều kiện cho bài kiểm tra đơn vị, chúng không phải là một phần của bài kiểm tra. Mocks, là một phần của bài kiểm tra, một phần của xác minh, một phần của việc đạt / không đạt.
Vì vậy, giả sử bạn có một phương thức nhận một đối tượng làm tham số. Bạn không bao giờ làm bất cứ điều gì thay đổi thông số này trong thử nghiệm. Bạn chỉ cần đọc một giá trị từ nó. Đó là một sơ khai.
Nếu bạn thay đổi bất cứ điều gì hoặc cần xác minh một số loại tương tác với đối tượng, thì đó là một mô hình.