Sự khác biệt giữa một mock & stub là gì?


963

Tôi đã đọc các bài viết khác nhau về chế độ nhạo báng và sơ khai trong thử nghiệm, bao gồm Mocks Ar't Arub của Martin Fowler , nhưng vẫn không hiểu sự khác biệt.



75
@OP Vì không có sự khác biệt. Bài viết này, được cộng đồng yêu thích, là - với tất cả sự tôn trọng - làm cho mọi thứ trở nên khó hiểu bằng cách thêm ý nghĩa bổ sung cho các từ dễ hiểu khác và làm cho mọi thứ trở nên phức tạp không cần thiết. Mock chỉ là một giả, một cái gì đó chạy logic kinh doanh giả thay vì thực. Kiểm tra hành vi cuối cùng là lựa chọn của bạn, nhưng nó vẫn là một giả. Hoặc bất cứ điều gì bạn muốn gọi nó, nhưng làm cho nó MỘT. Đừng chẻ một sợi lông. Giữ cho nó đơn giản, để mọi người có thể hiểu khái niệm của bạn một cách dễ dàng - điều mà bài viết trên không thành công.
WST

10
"Phân loại giữa giả, giả, và sơ khai rất không nhất quán trong các tài liệu." Với nhiều trích dẫn. Vẫn là một trong những trích dẫn Wikipedia yêu thích của tôi - nếu một thứ như vậy tồn tại :) en.wikipedia.org/wiki/Mock_object
JD.

11
rằng bài viết của Martin Fowler thực sự khó hiểu cho người mới bắt đầu.
lmiguelvargasf

1
Theo cách tôi hiểu thì một cuống sẽ chỉ là một đối tượng vứt đi cho bài kiểm tra của bạn, giống như một bộ sưu tập dữ liệu giả. Mock sẽ là phiên bản được ghi đè thông minh của một thứ phức tạp hơn, như lớp dịch vụ với nhiều phương thức khác nhau, mà bạn có thể đã thay đổi hành vi, cho các thử nghiệm của mình. Hai thứ được sử dụng cùng nhau, giống như bạn có thể chuyển một số vật thể còn sơ khai vào lớp bị nhạo báng của mình.
JsonStatham

Câu trả lời:


746

Sơ khai

Tôi tin rằng sự khác biệt lớn nhất là một sơ khai mà bạn đã viết với hành vi được xác định trước. Vì vậy, bạn sẽ có một lớp thực hiện sự phụ thuộc (rất có thể là lớp trừu tượng hoặc giao diện) mà bạn đang giả mạo cho mục đích thử nghiệm và các phương thức sẽ được đặt ra với các phản hồi đã đặt. Họ sẽ không làm bất cứ điều gì lạ mắt và bạn đã viết mã gốc cho nó bên ngoài bài kiểm tra của bạn.

Chế nhạo

Giả là một phần mà trong bài kiểm tra của bạn, bạn phải thiết lập với mong đợi của mình. Một bản giả không được thiết lập theo cách được xác định trước để bạn có mã thực hiện trong bài kiểm tra của mình. Giả định theo cách được xác định trong thời gian chạy vì mã đặt kỳ vọng phải chạy trước khi chúng làm bất cứ điều gì.

Sự khác biệt giữa Mocks và Stub

Các thử nghiệm được viết bằng giả thường theo một initialize -> set expectations -> exercise -> verifymẫu để thử nghiệm. Trong khi sơ khai viết sẵn sẽ theo một initialize -> exercise -> verify.

Sự tương đồng giữa Mocks và Stub

Mục đích của cả hai là loại bỏ kiểm tra tất cả các phụ thuộc của một lớp hoặc hàm để các kiểm tra của bạn tập trung hơn và đơn giản hơn trong những gì chúng đang cố gắng chứng minh.


876

Lời tựa

Có một số định nghĩa về các đối tượng, không có thật. Thuật ngữ chung là kiểm tra gấp đôi . Thuật ngữ này bao gồm: giả , giả , còn sơ khai , giả .

Tài liệu tham khảo

Theo bài viết của Martin Fowler :

  • Đối tượng giả được truyền xung quanh nhưng không bao giờ thực sự được sử dụng. Thông thường chúng chỉ được sử dụng để điền vào danh sách tham số.
  • Các đối tượng giả thực sự có các triển khai làm việc, nhưng thường sử dụng một số phím tắt khiến chúng không phù hợp để sản xuất (một cơ sở dữ liệu trong bộ nhớ là một ví dụ tốt).
  • Sơ khai cung cấp câu trả lời đóng hộp cho các cuộc gọi được thực hiện trong quá trình thử nghiệm, thường không phản hồi hoàn toàn cho bất kỳ điều gì bên ngoài những gì được lập trình trong thử nghiệm. Sơ khai cũng có thể ghi lại thông tin về các cuộc gọi, chẳng hạn như một cổng thông tin email còn nhớ các tin nhắn mà nó 'đã gửi' hoặc có thể chỉ có bao nhiêu tin nhắn mà nó 'đã gửi'.
  • Giả là những gì chúng ta đang nói ở đây: các đối tượng được lập trình sẵn với những kỳ vọng hình thành nên một đặc điểm kỹ thuật của các cuộc gọi mà chúng dự kiến ​​sẽ nhận được.

Phong cách

Mocks vs Stub = Thử nghiệm hành vi so với thử nghiệm Nhà nước

Nguyên tắc

Theo nguyên tắc Kiểm tra chỉ một điều trong mỗi bài kiểm tra , có thể có một số sơ khai trong một bài kiểm tra, nhưng nhìn chung chỉ có một bản giả.

Vòng đời

Kiểm tra vòng đời với cuống:

  1. Thiết lập - Chuẩn bị đối tượng đang được thử nghiệm và các cộng tác viên sơ khai của nó.
  2. Bài tập - Kiểm tra chức năng.
  3. Xác minh trạng thái - Sử dụng các xác nhận để kiểm tra trạng thái của đối tượng.
  4. Teardown - Dọn dẹp tài nguyên.

Kiểm tra vòng đời với giả:

  1. Cài đặt dữ liệu - Chuẩn bị đối tượng đang được thử nghiệm.
  2. Thiết lập các kỳ vọng - Chuẩn bị các kỳ vọng trong giả được sử dụng bởi đối tượng chính.
  3. Bài tập - Kiểm tra chức năng.
  4. Xác minh kỳ vọng - Xác minh rằng các phương thức chính xác đã được gọi trong giả.
  5. Xác minh trạng thái - Sử dụng các xác nhận để kiểm tra trạng thái của đối tượng.
  6. Teardown - Dọn dẹp tài nguyên.

Tóm lược

Cả thử nghiệm giả và sơ khai đều đưa ra câu trả lời cho câu hỏi: Kết quả là gì?

Thử nghiệm với giả cũng quan tâm: Làm thế nào đạt được kết quả?


Chờ đã, giả cũng trả lại câu trả lời đóng hộp? Nguyên nhân khác tại sao họ trả lời câu hỏi?
AturSams

Từ những gì bạn đã viết, tôi có thể nói rằng mocks = stub + kỳ vọng và xác minh, bởi vì mocks "cung cấp câu trả lời đóng hộp cho các cuộc gọi được thực hiện trong bài kiểm tra, thường không phản hồi tất cả mọi thứ bên ngoài những gì được lập trình trong bài kiểm tra" (giống như sơ khai). Và ví dụ mà Fowler thể hiện là ví dụ về một cuống thực sự là ví dụ về một điệp viên! Điều đó có nghĩa là một mock là một sơ khai, và một gián điệp là một sơ khai. Và còn sơ khai chỉ là một đối tượng có một số phương thức làm việc. Điều đó cũng giải thích tại sao Mockito không dùng phương thức stub ().
kolobok

Điều tôi cảm thấy khó hiểu về điều này và câu trả lời được chấp nhận là điều này kỳ vọng của người đặt ra, đó là gì? Thông thường, trong mã chính của người dùng, bạn tạo ra kết quả mà bạn mong đợi. Dường như bạn đặt kỳ vọng bằng cách nào đó VÀO đối tượng giả, tuy nhiên, điều đó không có ý nghĩa với tôi. CSONG, bạn có thể dễ dàng thực hiện giả với một số đầu vào, lưu trữ kết quả, tạo ra những kỳ vọng, sau đó so sánh. Bạn sử dụng thuật ngữ mà tôi thấy quá trừu tượng và mơ hồ.
IceFire

365

Một cuống là một đối tượng giả đơn giản. Nó chỉ đảm bảo kiểm tra chạy trơn tru.
Một mock là một sơ khai thông minh hơn. Bạn xác minh bài kiểm tra của bạn đi qua nó.


33
Tôi nghĩ rằng đây là câu trả lời ngắn gọn nhất. Takeaway: một mock IS-A còn sơ khai. stackoverflow.com/a/17810004/2288628 là phiên bản dài hơn của câu trả lời này.
PoweredByRice

8
Tôi không nghĩ rằng một giả là một sơ khai. Giả được sử dụng để khẳng định và không bao giờ nên trả lại dữ liệu, sơ khai được sử dụng để trả về dữ liệu và không bao giờ nên xác nhận.
dave1010

2
@ dave1010 Mocks chắc chắn nhất có thể trả về dữ liệu hoặc thậm chí ném ngoại lệ. Họ nên làm như vậy để đáp lại các thông số được truyền vào chúng.
Trenton

2
@trenton nếu một đối tượng trả lại hoặc ném dựa trên dữ liệu được truyền vào thì đó là giả , không phải là giả. Sơ khai kiểm tra cách SUT của bạn xử lý nhận tin nhắn, giả kiểm tra cách SUT của bạn gửi tin nhắn. Trộn lẫn 2 có thể dẫn đến thiết kế OO xấu.
dave1010

8
Tôi nghĩ rằng điều này là tuyệt vời - một sơ khai trả lời câu hỏi. Một giả cũng trả lại câu trả lời cho các câu hỏi (vẫn còn sơ khai) nhưng nó cũng xác minh rằng câu hỏi đã được hỏi !!
Leif

238

Dưới đây là một mô tả của từng người theo sau với mẫu thế giới thực.

  • Dummy - chỉ là giá trị không có thật để đáp ứng API.

    Ví dụ : Nếu bạn đang thử nghiệm một phương thức của một lớp yêu cầu nhiều tham số bắt buộc trong hàm tạo không có tác dụng đối với thử nghiệm của bạn, thì bạn có thể tạo các đối tượng giả cho mục đích tạo các thể hiện mới của một lớp.

  • Fake - tạo một triển khai thử nghiệm của một lớp có thể có sự phụ thuộc vào một số cơ sở hạ tầng bên ngoài. (Đó là thông lệ tốt khi kiểm tra đơn vị của bạn KHÔNG thực sự tương tác với cơ sở hạ tầng bên ngoài.)

    Ví dụ : Tạo triển khai giả để truy cập cơ sở dữ liệu, thay thế nó bằng in-memorybộ sưu tập.

  • Sơ khai - các phương thức ghi đè để trả về các giá trị được mã hóa cứng, còn được gọi là state-based.

    Ví dụ : Lớp kiểm tra của bạn phụ thuộc vào một phương thức Calculate()mất 5 phút để hoàn thành. Thay vì chờ trong 5 phút, bạn có thể thay thế triển khai thực sự của nó bằng sơ khai trả về các giá trị được mã hóa cứng; chỉ mất một phần nhỏ thời gian

  • Mock - rất giống với Stubnhưng interaction-basedthay vì dựa trên nhà nước. Điều này có nghĩa là bạn không mong đợi Mocktrả về một số giá trị, nhưng giả sử rằng thứ tự cụ thể của các cuộc gọi phương thức được thực hiện.

    Ví dụ: Bạn đang kiểm tra một lớp đăng ký người dùng. Sau khi gọi Save, nó nên gọi SendConfirmationEmail.

StubsMocksthực sự là các loại phụ của Mock, cả trao đổi thực hiện thực tế với thực hiện thử nghiệm, nhưng vì những lý do cụ thể, khác nhau.


175

Trong khóa học Codechool.com , Rails tests for Zombies , họ đưa ra định nghĩa này về các điều khoản:

Sơ khai

Để thay thế một phương thức bằng mã trả về một kết quả được chỉ định.

Chế nhạo

Một sơ khai với một khẳng định rằng phương thức được gọi.

Vì vậy, như Sean Strenhaver đã mô tả trong câu trả lời của mình, sự khác biệt là các giả định đặt kỳ vọng (nghĩa là đưa ra các xác nhận, về việc chúng có được gọi hay không).


Để bổ sung cho bài viết của Dillon, hãy nghĩ về điều này, bạn có một Lớp gọi là "MakeACake", người lấy một số thư viện: Sữa, Trứng, Đường, Lò nướng.
aarkerio

139

Sơ khai không thất bại bài kiểm tra của bạn, giả có thể.


2
Và tôi nghĩ điều này là tốt, bạn biết nếu các bài kiểm tra có hành vi tương tự sau khi tái cấu trúc.
RodriKing

1
@RodriKing Mình cũng có cảm giác như vậy. Như với Mock, với bất kỳ thay đổi nào trong mã sản xuất - bạn có các thay đổi tương ứng với mã kiểm tra. Đó là nỗi đau! Với Stub, có cảm giác như bạn tiếp tục kiểm tra hành vi để không cần thay đổi vi mô với mã kiểm tra.
tucq88

35

Tôi nghĩ rằng câu trả lời đơn giản và rõ ràng hơn về câu hỏi này được đưa ra từ Roy Osherove trong cuốn sách Nghệ thuật kiểm tra đơn vị (trang 85)

Cách dễ nhất để nói rằng chúng ta đang đối phó với một sơ khai là nhận thấy rằng sơ khai không bao giờ có thể thất bại trong bài kiểm tra. Các khẳng định sử dụng thử nghiệm luôn luôn chống lại lớp đang thử nghiệm.

Mặt khác, thử nghiệm sẽ sử dụng một đối tượng giả để xác minh xem thử nghiệm có thất bại hay không. [...]

Một lần nữa, đối tượng giả là đối tượng chúng ta sử dụng để xem thử nghiệm thất bại hay không.

Điều đó có nghĩa là nếu bạn đang xác nhận chống lại giả, điều đó có nghĩa là bạn đang sử dụng giả như một bản giả, nếu bạn chỉ sử dụng giả để chạy thử mà không xác nhận rằng bạn đang sử dụng giả như một cuống.


2
Tôi muốn câu trả lời của bạn sẽ tìm thấy nó lên đầu. Đây là R. Osherove giải thích điều này youtu.be/fAb_OnooCsQ?t=1006 .
Michael Ekoka

31

Đọc tất cả các giải thích ở trên, hãy để tôi cố gắng cô đọng:

  • Sơ khai : một đoạn mã giả cho phép chạy thử, nhưng bạn không quan tâm điều gì xảy ra với nó.
  • Mock : một đoạn mã giả, mà bạn XÁC MINH được gọi chính xác như là một phần của bài kiểm tra.
  • Spy : một đoạn mã giả, chặn một số cuộc gọi đến một đoạn mã thực, cho phép bạn xác minh các cuộc gọi mà không cần thay thế toàn bộ đối tượng ban đầu.

4
Câu trả lời tốt. Mock nghe khá giống với Spy, dựa trên định nghĩa của bạn. Sẽ rất tuyệt nếu bạn cập nhật câu trả lời của mình để bao gồm thêm một vài lần kiểm tra.
Rowan Gontier

Tôi đã không nghe nói về Spy khi tôi viết câu trả lời này.
O'Rooney

23

Một Mock chỉ là kiểm tra hành vi, đảm bảo các phương thức nhất định được gọi. Stub là phiên bản có thể kiểm tra (per se) của một đối tượng cụ thể.

Bạn có ý nghĩa gì theo cách của Apple?


19
"Ý bạn là gì theo cách của Apple?" Sử dụng Helvetica
kubi

7
Theo cách của Apple trái ngược với cách của Microsoft :)
never_had_a_name

2
Điều này có giúp gì cho tình hình không?
Tinh vân

21

Nếu bạn so sánh nó với gỡ lỗi:

Sơ khai giống như đảm bảo một phương thức trả về giá trị đúng

Mock giống như thực sự bước vào phương thức và đảm bảo mọi thứ bên trong đều chính xác trước khi trả về giá trị chính xác.


20

Sử dụng một mô hình tinh thần thực sự giúp tôi hiểu điều này, hơn là tất cả những lời giải thích và bài báo, điều đó không hoàn toàn "chìm đắm".

Hãy tưởng tượng con bạn có một tấm kính trên bàn và nó bắt đầu chơi với nó. Bây giờ, bạn sợ nó sẽ vỡ. Vì vậy, bạn đưa cho anh ta một tấm nhựa thay thế. Đó sẽ là một Mock (cùng hành vi, cùng giao diện, thực hiện "nhẹ nhàng" hơn).

Bây giờ, giả sử bạn không có thay thế nhựa, vì vậy bạn giải thích "Nếu bạn tiếp tục chơi với nó, nó sẽ bị hỏng!". Đó là một Stub , bạn đã cung cấp một trạng thái được xác định trước.

Một hình nộm sẽ là cái nĩa mà anh ta thậm chí không sử dụng ... và một Spy có thể giống như đưa ra lời giải thích giống như bạn đã sử dụng.


19

Tôi nghĩ rằng sự khác biệt quan trọng nhất giữa họ là ý định của họ.

Hãy để tôi cố gắng giải thích nó trong TẠI SAO còn sơ khai so với TẠI SAO giả

Giả sử tôi đang viết mã kiểm tra cho bộ điều khiển dòng thời gian công khai của khách hàng twitter của tôi

Đây là mã mẫu thử nghiệm

twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
  • STUB: Kết nối mạng với API twitter rất chậm, khiến thử nghiệm của tôi chậm. Tôi biết nó sẽ trả về các mốc thời gian, vì vậy tôi đã tạo ra một API HTTP mô phỏng sơ khai, để thử nghiệm của tôi sẽ chạy rất nhanh và tôi có thể chạy thử nghiệm ngay cả khi tôi ngoại tuyến.
  • MOCK: Tôi chưa viết bất kỳ phương thức UI nào của mình và tôi không chắc mình cần viết phương thức nào cho đối tượng ui của mình. Tôi hy vọng biết làm thế nào bộ điều khiển của tôi sẽ cộng tác với đối tượng ui của tôi bằng cách viết mã kiểm tra.

Bằng cách viết giả, bạn khám phá mối quan hệ cộng tác của các đối tượng bằng cách xác minh sự mong đợi được đáp ứng, trong khi sơ khai chỉ mô phỏng hành vi của đối tượng.

Tôi khuyên bạn nên đọc bài viết này nếu bạn đang cố gắng để biết thêm về giả: http://jmock.org/oopsla2004.pdf


1
Tôi nghĩ bạn có ý tưởng đúng, nhưng Dillon Kearns đã giải thích nó rõ ràng hơn rất nhiều.
O'Rooney

19

Rất rõ ràng và thiết thực:

Sơ khai: Một lớp hoặc đối tượng thực hiện các phương thức của lớp / đối tượng được làm giả và trả về luôn những gì bạn muốn.

Ví dụ trong JavaScript:

var Stub = {
   method_a: function(param_a, param_b){
      return 'This is an static result';
   }
}

Mock: Giống như sơ khai, nhưng nó thêm một số logic "xác minh" khi một phương thức được gọi để bạn có thể chắc chắn rằng một số triển khai đang gọi phương thức đó.

Như @mLevan nói hãy tưởng tượng như một ví dụ rằng bạn đang kiểm tra một lớp đăng ký người dùng. Sau khi gọi Save, nó sẽ gọi SendConf ConfirmationEmail.

Một mã rất ngu ngốc Ví dụ:

var Mock = {
   calls: {
      method_a: 0
   }

   method_a: function(param_a, param_b){
     this.method_a++; 
     console.log('Mock.method_a its been called!');
   }
}

16

Slide này giải thích sự khác biệt chính rất tốt.

nhập mô tả hình ảnh ở đây

* Từ CSE 403 Bài 16, Đại học Washington (slide được tạo bởi "Marty Stepp")


Đây là lời giải thích rõ ràng hơn về sự khác biệt giữa hai, IMO. Đối với sơ khai: người kiểm tra lấy Sơ khai và sử dụng trực tiếp bên trong lớp được kiểm tra. Nhưng đối với Mock, người kiểm tra phải thiết bị cách đối tượng Mock sẽ được sử dụng. Trong các trường hợp khác nhau, nó sẽ hành xử khác nhau. Ngược lại, stub dự kiến ​​sẽ không hoạt động khác nhưng được sử dụng như vậy (nghĩa là trả lại cùng một dữ liệu bất cứ khi nào được liên lạc)
Dexter

12

Tôi thích lời giải thích được đưa ra bởi Roy Osherove [liên kết video] .

Mỗi lớp hoặc đối tượng được tạo là Fake. Đó là một Mock nếu bạn xác minh các cuộc gọi chống lại nó. Nếu không thì nó vẫn còn sơ khai.


12
  • Sơ khai so với Mocks
    • Sơ khai
      1. cung cấp câu trả lời cụ thể cho các cuộc gọi phương thức
        • ví dụ: myStubbedService.getValues ​​() chỉ cần trả về một Chuỗi cần thiết theo mã đang kiểm tra
      2. được sử dụng bởi mã đang thử nghiệm để cô lập nó
      3. không thể thi trượt
        • ví dụ: myStubbedService.getValues ​​() chỉ trả về giá trị gốc
      4. thường thực hiện các phương pháp trừu tượng
    • Giả
      1. "superset" của sơ khai; có thể khẳng định rằng các phương thức nhất định được gọi
        • ví dụ: xác minh rằng myMockedService.getValues ​​() chỉ được gọi một lần
      2. Được sử dụng để kiểm tra hành vi của mã đang thử nghiệm
      3. có thể trượt bài kiểm tra
        • ví dụ: xác minh rằng myMockedService.getValues ​​() đã được gọi một lần; xác minh thất bại, vì myMockedService.getValues ​​() không được gọi bởi mã thử nghiệm của tôi
      4. giao diện thường giả

11

hãy xem Kiểm tra nhân đôi:

  • Giả mạo : Hàng giả là những đối tượng có triển khai hoạt động, nhưng không giống như sản xuất. Chẳng hạn như : triển khai trong bộ nhớ của Đối tượng truy cập dữ liệu hoặc Kho lưu trữ.
  • Stub : Stub là một đối tượng chứa dữ liệu được xác định trước và sử dụng nó để trả lời các cuộc gọi trong các thử nghiệm. Chẳng hạn như : một đối tượng cần lấy một số dữ liệu từ cơ sở dữ liệu để đáp ứng với một cuộc gọi phương thức.

  • Mocks : Mocks là đối tượng đăng ký các cuộc gọi mà họ nhận được. Trong xác nhận thử nghiệm, chúng tôi có thể xác minh trên Mocks rằng tất cả các hành động dự kiến ​​đã được thực hiện. Chẳng hạn như : một chức năng gọi dịch vụ gửi e-mail. để biết thêm chỉ cần kiểm tra này .


1
Câu trả lời hay nhất theo ý kiến ​​của tôi
Ero Stefano

9

Một giả là một thuật ngữ chung có thể được sử dụng để mô tả một trong hai vẫn còn sơ khai hoặc một đối tượng giả (viết tay hay cách khác), vì cả hai đều trông giống như các đối tượng thực sự.

Việc giả mạo là sơ khai hay giả, tùy thuộc vào cách sử dụng trong thử nghiệm hiện tại. Nếu nó được sử dụng để kiểm tra sự tương tác (được khẳng định), thì đó là một đối tượng giả. Nếu không, nó vẫn còn sơ khai.

Fakes đảm bảo kiểm tra chạy trơn tru. Điều đó có nghĩa là người đọc bài kiểm tra trong tương lai của bạn sẽ hiểu hành vi của đối tượng giả, sẽ không cần đọc mã nguồn của nó (mà không cần phụ thuộc vào tài nguyên bên ngoài).

Kiểm tra chạy trơn tru có nghĩa là gì?
Forexample trong mã dưới đây:

 public void Analyze(string filename)
        {
            if(filename.Length<8)
            {
                try
                {
                    errorService.LogError("long file entered named:" + filename);
                }
                catch (Exception e)
                {
                    mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
                }
            }
        }

Bạn muốn kiểm tra phương thức mailService.SendEMail () , để làm điều đó bạn cần mô phỏng Ngoại lệ trong phương thức kiểm tra của mình, vì vậy bạn chỉ cần tạo lớp FakeSub của Fake Stub để mô phỏng kết quả đó, sau đó mã kiểm tra của bạn sẽ có thể kiểm tra phương thức mailService.SendEMail (). Như bạn thấy, bạn cần mô phỏng một kết quả từ một lớp ErrorService phụ thuộc bên ngoài khác.


8

Ngay từ các Mock Roles giấy , không phải đối tượng , bởi các nhà phát triển của jMock:

Sơ khai là các triển khai giả của mã sản xuất trả về kết quả đóng hộp. Các đối tượng giả hoạt động như các cuống, nhưng cũng bao gồm các xác nhận để xác định các tương tác của đối tượng đích với các đối tượng lân cận.

Vì vậy, sự khác biệt chính là:

  • các kỳ vọng được đặt trên các sơ khai thường là chung chung, trong khi các kỳ vọng được đặt trên các giả có thể "thông minh" hơn (ví dụ: trả lại điều này trong cuộc gọi đầu tiên, điều này vào lần thứ hai, v.v.).
  • khai chủ yếu được sử dụng để thiết lập đầu vào gián tiếp của SUT , trong khi mocks có thể được sử dụng để kiểm tra cả hai đầu vào gián tiếp và gián tiếp đầu ra của SUT.

Tóm lại, trong khi cũng cố gắng phân tán sự nhầm lẫn từ tiêu đề bài viết của Fowler : giả là cuống, nhưng chúng không chỉ là cuống .


1
Tôi nghĩ bạn đã đúng, nhưng đây là lý do tại sao bài viết của Fowler gây nhầm lẫn, tiêu đề bài viết là "Mocks Ar't Stub" ... nhưng chúng là?! _ (ツ) _ /
ném đá

@stonedauwg, thực sự, tôi đã chỉnh sửa bài viết của mình để kết hợp cách chơi chữ của bạn và làm rõ. Hy vọng điều này sẽ giúp một chút nữa.
Dimos

@stonedauwg, một bản giả không phải là sơ khai, giống như hình chữ nhật không phải là hình vuông. :)
seanriordan08

7

Tôi đã đọc Nghệ thuật kiểm tra đơn vị , và tình cờ thấy định nghĩa sau:

Một giả là một thuật ngữ chung có thể được sử dụng để mô tả một trong hai vẫn còn sơ khai hoặc một đối tượng giả (viết tay hay cách khác), vì cả hai đều trông giống như các đối tượng thực sự. Việc giả mạo là sơ khai hay giả, tùy thuộc vào cách sử dụng trong thử nghiệm hiện tại. nếu nó được sử dụng để kiểm tra sự tương tác (được khẳng định), thì đó là một đối tượng giả . Nếu không, nó vẫn còn sơ khai .


5

Tôi đã xem qua bài viết thú vị này của chúBob The Little Mocker . Nó giải thích tất cả các thuật ngữ theo cách rất dễ hiểu, vì vậy nó hữu ích cho người mới bắt đầu. Bài viết của Martin Fowlers là một bài khó đọc đặc biệt là cho những người mới bắt đầu như tôi.


4

Sơ khai giúp chúng tôi chạy thử nghiệm. Làm sao? Nó cung cấp các giá trị giúp chạy thử nghiệm. Các giá trị này tự nó không có thật và chúng tôi đã tạo các giá trị này chỉ để chạy thử nghiệm. Ví dụ: chúng tôi tạo HashMap để cung cấp cho chúng tôi các giá trị tương tự với các giá trị trong bảng cơ sở dữ liệu. Vì vậy, thay vì tương tác trực tiếp với cơ sở dữ liệu, chúng tôi tương tác với Hashmap.

Mock là một đối tượng giả chạy thử nghiệm. nơi chúng tôi đặt khẳng định.


"Vì vậy, thay vì tương tác trực tiếp với cơ sở dữ liệu, chúng tôi tương tác với Hashmap." ... bởi vì họ chưa có thời gian để mã hóa Mô-đun cơ sở dữ liệu và chúng tôi không thể chạy mã kiểm tra mà không sử dụng sơ khai. Nếu không, Hasmap rất giống nhau sẽ là một bản giả! đúng?
Boris Däppen

4

Xem ví dụ bên dưới về mô phỏng so với sơ khai sử dụng khung C # và Moq. Moq không có từ khóa đặc biệt cho Stub nhưng bạn cũng có thể sử dụng đối tượng Mock để tạo sơ khai.

namespace UnitTestProject2
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));

            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
        }
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(0);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
        }
        /// <summary>
        /// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
        {
            // Arrange 
            var stubEntityRepository = new Mock<IEntityRepository>();
            stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
                .Returns("Stub");
            const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
            var entity = new EntityClass(stubEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
        }
    }
    public class EntityClass
    {
        private IEntityRepository _entityRepository;
        public EntityClass(IEntityRepository entityRepository)
        {
            this._entityRepository = entityRepository;
        }
        public string Name { get; set; }
        public string GetNameWithPrefix(int id)
        {
            string name = string.Empty;
            if (id > 0)
            {
                name = this._entityRepository.GetName(id);
            }
            return "Mr. " + name;
        }
    }
    public interface IEntityRepository
    {
        string GetName(int id);
    }
    public class EntityRepository:IEntityRepository
    {
        public string GetName(int id)
        {
            // Code to connect to DB and get name based on Id
            return "NameFromDb";
        }
    }
}

4

Quan điểm thử nghiệm còn sơ khai và Mock:

  • Stub là triển khai giả được thực hiện bởi người dùng theo cách tĩnh có nghĩa là trong Stub viết mã thực hiện. Vì vậy, nó không thể xử lý định nghĩa dịch vụ và điều kiện động, Thông thường, điều này được thực hiện trong khung JUnit mà không sử dụng khung mô phỏng.

  • Mock cũng là triển khai giả nhưng việc triển khai thực hiện theo cách năng động bằng cách sử dụng các khung Mocking như Mockito. Vì vậy, chúng ta có thể xử lý điều kiện và định nghĩa dịch vụ theo cách động, tức là các giả có thể được tạo động từ mã khi chạy. Vì vậy, bằng cách sử dụng giả, chúng ta có thể thực hiện Stub một cách linh hoạt.


3

Cộng với câu trả lời hữu ích, Một trong những điểm mạnh nhất của việc sử dụng Mocks so với Dự bị

Nếu cộng tác viên [mà mã chính phụ thuộc vào nó] không thuộc quyền kiểm soát của chúng tôi (ví dụ: từ thư viện của bên thứ ba), thì
trong trường hợp này, sơ khai khó viết hơn là giả .


2

Tôi đã sử dụng các ví dụ python trong câu trả lời của tôi để minh họa sự khác biệt.

Stub - Stubbing là một kỹ thuật phát triển phần mềm được sử dụng để thực hiện các phương thức của các lớp sớm trong vòng đời phát triển. Chúng được sử dụng phổ biến như giữ chỗ để thực hiện giao diện đã biết, trong đó giao diện được hoàn thiện hoặc được biết nhưng việc triển khai chưa được biết hoặc hoàn tất. Bạn bắt đầu với sơ khai, điều đó có nghĩa đơn giản là bạn chỉ viết định nghĩa của hàm xuống và để lại mã thực tế cho sau này. Ưu điểm là bạn sẽ không quên các phương thức và bạn có thể tiếp tục suy nghĩ về thiết kế của mình trong khi xem nó trong mã. Bạn cũng có thể để sơ khai của bạn trả về một phản hồi tĩnh để các phản hồi có thể được sử dụng bởi các phần khác trong mã của bạn ngay lập tức. Các đối tượng còn sơ khai cung cấp một phản hồi hợp lệ, nhưng nó tĩnh cho dù bạn nhập thông tin nào, bạn sẽ luôn nhận được cùng một phản hồi:

class Foo(object):
    def bar1(self):
        pass

    def bar2(self):
        #or ...
        raise NotImplementedError

    def bar3(self):
        #or return dummy data
        return "Dummy Data"

Các đối tượng giả được sử dụng trong các trường hợp thử nghiệm giả, chúng xác nhận rằng các phương thức nhất định được gọi trên các đối tượng đó. Các đối tượng giả là các đối tượng mô phỏng bắt chước hành vi của các đối tượng thực theo các cách được kiểm soát. Bạn thường tạo một đối tượng giả để kiểm tra hành vi của một số đối tượng khác. Mocks cho phép chúng tôi mô phỏng các tài nguyên không có sẵn hoặc quá khó sử dụng để thử nghiệm đơn vị.

mymodule.py:

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

kiểm tra:

from mymodule import rm
import mock
import unittest

class RmTestCase(unittest.TestCase):
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")

if __name__ == '__main__':
    unittest.main()

Đây là một ví dụ rất cơ bản chỉ chạy rm và xác nhận tham số mà nó được gọi. Bạn có thể sử dụng giả với các đối tượng không chỉ là các chức năng như được hiển thị ở đây và bạn cũng có thể trả về một giá trị để có thể sử dụng một đối tượng giả để thay thế một sơ khai để kiểm tra.

Thông tin thêm về unittest.mock , lưu ý trong python 2.x mock không được bao gồm trong unittest nhưng là một mô-đun có thể tải xuống có thể được tải xuống qua pip (pip install mock).

Tôi cũng đã đọc "Nghệ thuật kiểm tra đơn vị" của Roy Osherove và tôi nghĩ sẽ thật tuyệt nếu một cuốn sách tương tự được viết bằng các ví dụ Python và Python. Nếu ai biết về một cuốn sách như vậy xin vui lòng chia sẻ. Chúc mừng :)


2

Sơ khai là một đối tượng giả được xây dựng cho mục đích thử nghiệm. Mock là một sơ khai ghi lại liệu các cuộc gọi dự kiến ​​có hiệu quả hay không.


2

Sơ khai là một chức năng trống được sử dụng để tránh các ngoại lệ chưa được xử lý trong các thử nghiệm:

function foo(){}

Giả là một chức năng nhân tạo được sử dụng để tránh phụ thuộc vào hệ điều hành, môi trường hoặc phần cứng trong các thử nghiệm:

function foo(bar){ window = this; return window.toString(bar); }

Về mặt khẳng định và nhà nước:

  • Giả được xác nhận trước khi một sự kiện hoặc thay đổi trạng thái
  • Sơ khai không được xác nhận, họ cung cấp trạng thái trước một sự kiện để tránh thực thi mã từ các đơn vị không liên quan
  • Các gián điệp được thiết lập giống như sơ khai, sau đó được xác nhận sau khi thay đổi trạng thái hoặc trạng thái
  • Các giả mạo không được xác nhận, chúng chạy sau một sự kiện với các phụ thuộc được mã hóa cứng để tránh trạng thái

Người giới thiệu


2
+1 để thêm điệp viên vào bảng chú giải. Ngoài ra, tôi nghĩ bạn có nghĩa là "Các gián điệp được thiết lập như giả" chứ không phải "Các gián điệp được thiết lập như sơ khai"
Sameh Deabes


2

Một giả là cả một đối tượng kỹ thuật và chức năng .

Các giả là kỹ thuật . Nó thực sự được tạo ra bởi một thư viện giả (EasyMock, JMockit và gần đây là Mockito được biết đến với những thứ này) nhờ vào việc tạo mã byte .
Việc thực hiện mô hình được tạo ra theo một cách mà chúng ta có thể cụ nó để trả về một giá trị cụ thể khi một phương pháp được gọi nhưng cũng có một số thứ khác như xác minh rằng một phương pháp mô hình được gọi với một số thông số cụ thể (kiểm tra nghiêm ngặt) hoặc bất cứ thông số ( không kiểm tra nghiêm ngặt).

Khởi tạo một bản giả:

@Mock Foo fooMock

Ghi lại một hành vi:

when(fooMock.hello()).thenReturn("hello you!");

Xác minh một lời mời:

verify(fooMock).hello()

Đây rõ ràng không phải là cách tự nhiên để khởi tạo / ghi đè lớp / hành vi Foo. Đó là lý do tại sao tôi đề cập đến một khía cạnh kỹ thuật.

Nhưng giả cũng có chức năng vì nó là một thể hiện của lớp mà chúng ta cần tách ra khỏi SUT. Và với các hành vi được ghi lại trên đó, chúng ta có thể sử dụng nó trong SUT theo cách tương tự như chúng ta sẽ làm với một sơ khai.


Sơ khai chỉ là một đối tượng chức năng : đó là một thể hiện của lớp mà chúng ta cần tách ra khỏi SUT và đó là tất cả. Điều đó có nghĩa là cả lớp sơ khai và tất cả các đồ đạc hành vi cần thiết trong các bài kiểm tra đơn vị của chúng tôi phải được xác định rõ ràng.
Ví dụ để stub hello()sẽ cần phải phân Foolớp lớp (hoặc thực hiện giao diện của nó, nó có nó) và ghi đè hello() :

public class HelloStub extends Hello{    
  public String hello { 
      return "hello you!"; 
  }
}

Nếu một kịch bản thử nghiệm khác yêu cầu trả về giá trị khác, có lẽ chúng ta sẽ cần xác định một cách chung để đặt trả về:

public class HelloStub extends Hello{    
  public HelloStub(String helloReturn){
       this.helloReturn = helloReturn;
  }
  public String hello { 
      return helloReturn; 
  }
}

Kịch bản khác: nếu tôi có một phương thức hiệu ứng phụ (không trả về) và tôi sẽ kiểm tra xem phương thức đó có được gọi hay không, tôi có lẽ nên thêm một boolean hoặc một bộ đếm trong lớp sơ khai để đếm số lần phương thức được gọi.


Phần kết luận

Sơ khai đòi hỏi nhiều chi phí / mã để viết cho bài kiểm tra đơn vị của bạn. Những gì giả ngăn cản nhờ cung cấp các tính năng ghi / xác minh ra khỏi hộp.
Đó là lý do tại sao ngày nay, phương pháp sơ khai hiếm khi được sử dụng trong thực tế với sự ra đời của các thư viện giả tuyệt vời.


Về Điều Martin Fowler: Tôi không nghĩ là một lập trình viên "nhạo báng" trong khi tôi sử dụng giả và tôi tránh sơ khai.
Nhưng tôi sử dụng giả khi thực sự cần thiết (phụ thuộc gây khó chịu) và tôi thích kiểm tra cắt lát và kiểm thử tích hợp mini khi tôi kiểm tra một lớp với các phụ thuộc mà chế độ chế nhạo sẽ là chi phí chung.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.