Mock framework so với MS Fakes framework


99

Một chút nhầm lẫn về sự khác biệt của các khuôn khổ Mock như NMock và VS 2011 Fakes Framework. Xem qua MSDN, những gì tôi hiểu là Fakes cho phép bạn mô phỏng các phụ thuộc của mình giống như RhinoMock hoặc NMock, tuy nhiên cách tiếp cận khác nhau, Fakes tạo mã để sử dụng chức năng này nhưng Mocks framework thì không. Vậy cách hiểu của tôi có đúng không? Giả mạo chỉ là một khuôn khổ giả mạo khác

Câu trả lời:


189

Câu hỏi của bạn là về cách khung MS Fakes khác với NMock và có vẻ như các câu trả lời khác đã giải quyết một số vấn đề đó, nhưng đây là một số thông tin khác về cách chúng giống nhau và chúng khác nhau như thế nào. NMock cũng tương tự như RhinoMocks và Moq, vì vậy tôi đang nhóm chúng lại với NMock.

Có 3 điểm khác biệt chính mà tôi thấy ngay giữa NMock / RhinoMocks / Moq và MS Fakes Framework:

  • Khung công tác giả mạo MS sử dụng mã được tạo, giống như Trình truy cập trong các phiên bản trước của Visual Studio thay vì các loại chung chung. Khi bạn muốn sử dụng khung giả mạo cho một phần phụ thuộc, bạn thêm hợp ngữ chứa phần phụ thuộc vào các tham chiếu của dự án thử nghiệm và sau đó nhấp chuột phải vào nó để tạo các bản sao thử nghiệm (sơ khai hoặc miếng chêm). Sau đó, khi bạn đang kiểm tra, bạn thực sự đang sử dụng các lớp được tạo này để thay thế. NMock sử dụng generic để thực hiện điều tương tự (tức là IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). Theo ý kiến ​​của tôi, cách tiếp cận khung MS Fakes hạn chế điều hướng mã và cấu trúc lại từ bên trong các thử nghiệm vì bạn thực sự đang làm việc với một lớp được tạo chứ không phải giao diện thực của bạn.

  • Khung làm việc giả mạo MS cung cấp sơ khai và nốt ruồi (miếng chêm), trong khi NMock, RhinoMocks và Moq đều cung cấp sơ khai và mô phỏng . Tôi thực sự không hiểu quyết định của MS khi không bao gồm những lời chế giễu và cá nhân tôi không phải là một fan hâm mộ của nốt ruồi vì những lý do được mô tả dưới đây.

  • Với khung công tác giả mạo MS, bạn cung cấp một triển khai thay thế của các phương pháp bạn muốn sơ khai. Trong các triển khai thay thế này, bạn có thể chỉ định các giá trị trả về và theo dõi thông tin về cách hoặc nếu phương thức được gọi. Với NMock, RhinoMocks và Moq, bạn tạo một đối tượng mô phỏng và sau đó sử dụng đối tượng đó để chỉ định các giá trị trả về sơ khai hoặc để theo dõi các tương tác (liệu các phương thức có được gọi hay không). Tôi thấy cách tiếp cận giả mạo MS phức tạp hơn và ít biểu đạt hơn.

Để làm rõ sự khác biệt về những gì các khung công tác cung cấp: NMock, RhinoMocks và Moq đều cung cấp hai loại thử nghiệm kép (sơ khai và mô phỏng). Khuôn khổ giả mạo cung cấp các phần khai và nốt ruồi (họ gọi chúng là miếng chêm) và không may là không bao gồm các phần giả. Để hiểu được sự khác biệt và giống nhau giữa NMock và MS Fakes, sẽ rất hữu ích nếu bạn hiểu các loại bộ đôi thử nghiệm khác nhau này là gì:

Stubs: Stubs được sử dụng khi bạn cần cung cấp giá trị cho các phương thức hoặc thuộc tính sẽ được yêu cầu nhân đôi thử nghiệm của bạn theo phương pháp đang thử nghiệm. Ví dụ: khi phương thức của tôi đang được thử nghiệm gọi phương thức DoesStudentExist () của thử nghiệm IStudentRepository kép, tôi muốn nó trả về true.

Ý tưởng về sơ khai trong NMock và MS giả là giống nhau, nhưng với NMock, bạn sẽ làm điều gì đó như sau:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

Và với MSFakes, bạn sẽ thực hiện một số cách như thế này:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Lưu ý rằng trong ví dụ MS Fakes, bạn tạo một triển khai hoàn toàn mới cho phương thức DoesStudentExist (Lưu ý rằng nó được gọi là DoesStudentExistInt32 vì khuôn khổ giả mạo gắn các kiểu dữ liệu tham số vào tên phương thức khi nó tạo ra các đối tượng sơ khai, tôi nghĩ điều này che khuất sự rõ ràng của Các bài kiểm tra). Thành thật mà nói, việc triển khai NMock cũng làm tôi khó chịu vì nó sử dụng một chuỗi để xác định tên phương thức. (Thứ lỗi cho tôi nếu tôi đã hiểu lầm cách NMock được dự định sử dụng.) Cách tiếp cận này thực sự hạn chế việc tái cấu trúc và tôi thực sự khuyên bạn nên sử dụng RhinoMocks hoặc Moq thay vì NMock vì lý do này.

Mocks: Mocks được sử dụng để xác minh sự tương tác giữa phương pháp của bạn đang được thử nghiệm và các phụ thuộc của nó. Với NMock, bạn thực hiện điều này bằng cách đặt các kỳ vọng tương tự như sau:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Đây là một lý do khác tại sao tôi thích RhinoMocks và Moq hơn NMock, NMock sử dụng kiểu kỳ vọng cũ hơn trong khi RhinoMocks và Moq đều hỗ trợ cách tiếp cận Sắp xếp / Hành động / Khẳng định trong đó bạn chỉ định các tương tác mong đợi dưới dạng xác nhận ở cuối thử nghiệm như thế này :

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Một lần nữa, hãy lưu ý rằng RhinoMocks sử dụng lambda thay vì một chuỗi để xác định phương thức. Khung công tác giả mạo ms hoàn toàn không cung cấp mô hình giả. Điều này có nghĩa là trong các triển khai sơ khai của bạn (xem mô tả sơ khai ở trên), bạn phải đặt các biến mà sau này bạn xác minh đã được đặt đúng chưa. Nó sẽ trông giống như thế này:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Tôi thấy cách tiếp cận này hơi phức tạp vì bạn phải theo dõi lệnh gọi trong sơ khai và sau đó xác nhận nó sau này trong thử nghiệm. Tôi thấy các ví dụ về NMock, và đặc biệt là RhinoMocks trở nên biểu cảm hơn.

Nốt ruồi (Shims): Thành thật mà nói, tôi không thích nốt ruồi, vì chúng có khả năng bị lợi dụng. Một trong những điều tôi rất thích về kiểm thử đơn vị (và đặc biệt là TDD) là kiểm tra mã của bạn sẽ giúp bạn hiểu nơi bạn đã viết mã kém. Điều này là do việc kiểm tra mã được viết kém rất khó. Điều này không đúng khi sử dụng nốt ruồi vì nốt ruồi thực sự được thiết kế để cho phép bạn kiểm tra sự phụ thuộc không được tiêm hoặc để kiểm tra các phương pháp riêng tư. Chúng hoạt động tương tự như sơ khai, ngoại trừ việc bạn sử dụng ShimsContext như sau:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Lo lắng của tôi với miếng chêm là mọi người sẽ bắt đầu coi chúng là "cách dễ dàng hơn để kiểm tra đơn vị" bởi vì nó không buộc bạn phải viết mã theo cách bạn nên làm. Để viết đầy đủ hơn về khái niệm này, hãy xem bài đăng này của tôi:

Để biết thêm thông tin về một số mối quan tâm liên quan đến khuôn khổ hàng giả, hãy xem các bài đăng sau:

Nếu bạn quan tâm đến việc học RhinoMocks, đây là video đào tạo Pluralsight (tiết lộ đầy đủ - tôi đã viết khóa học này và được trả tiền bản quyền cho lượt xem, nhưng tôi nghĩ nó áp dụng cho cuộc thảo luận này nên tôi sẽ đưa nó vào đây):


14
@Jim Tôi không đồng ý rằng cho phép một "kiểm tra chống lại các phụ thuộc không được tiêm vào" là một điều tồi tệ. Ngược lại, trong thực tế, khả năng này giúp tránh làm lộn xộn một cơ sở mã với nhiều giao diện vô nghĩa và cấu hình / độ phức tạp liên quan đến "hệ thống dây đối tượng". Tôi đã thấy một vài dự án (cả Java và .NET) có hàng trăm giao diện Java / C # chỉ với một lớp triển khai duy nhất và rất nhiều cấu hình XML vô dụng (đối với đậu Spring DI hoặc trong Web.config đối với MS Unity khuôn khổ). Những phụ thuộc như vậy tốt hơn là không được tiêm vào.
Rogério

19
@Rogerio, tôi đã làm việc trên nhiều dự án với hàng nghìn lớp tuân theo mô hình này và khi việc chèn phụ thuộc được thực hiện đúng cách (nếu bạn đang sử dụng cấu hình XML thì bạn đang làm không đúng, imho), việc này rất đơn giản và tương đối dễ dàng. Mặt khác, tôi đã làm việc trên các hệ thống không làm được điều này và việc ghép nối giữa các lớp luôn khiến tôi đau đầu. Thử nghiệm không phải là lý do để sử dụng tiêm phụ thuộc (mặc dù nó không phải là lý do xấu). Nguyên nhân thực sự là do khớp nối bị lỏng. Có những giao diện được thực hiện bởi một lớp chưa bao giờ khiến tôi đau đầu.
Jim Cooper

17
@Jim, tôi thấy khả năng kiểm tra thiết kế xấu là một điểm cộng tuyệt vời. Điều đầu tiên bạn muốn làm trước khi cấu trúc lại mã kế thừa để hướng tới một thiết kế tốt hơn, là viết thử nghiệm.
Thomas Materna

4
Quy tắc của tôi là tôi chỉ sử dụng Fakes khi tôi phải Shim một phương thức chia sẻ / tĩnh hoặc một lớp mà tôi không có quyền truy cập để triển khai một giao diện. Đối với mọi thứ khác, tôi sử dụng Moq. Hàng giả chậm hơn (vì cần tạo các cụm giả mạo) và cần nhiều nỗ lực mã hóa hơn để phù hợp với chức năng mà bạn có được với Moq.
Nick

5
@ CarloV.Dango Trước hết, tôi hoàn toàn biết rằng Dependency Injection không yêu cầu các giao diện riêng biệt; bình luận trước đây của tôi chỉ ám chỉ đến xu hướng tạo các giao diện như vậy khi sử dụng DI. Thứ hai, "IOC" (Inversion of Control) không liên quan gì đến DI! (Đầu tiên là về sự đảo ngược luồng điều khiển giữa các phương thức, trong khi thứ hai là về việc giải quyết các phụ thuộc cho một thành phần / đối tượng.) Bằng cách nhầm lẫn giữa IOC và DI, đó là bạn đang thể hiện sự thiếu hiểu biết, không phải tôi; và, thành thật mà nói, trang web này không đáng để mọi người xúc phạm người khác, vì vậy làm ơn, hãy giữ cho nó văn minh.
Rogério

23

Bạn đúng, nhưng câu chuyện còn nhiều điều hơn thế. Những điều quan trọng nhất cần rút ra từ câu trả lời này là:

  1. Kiến trúc của bạn nên sử dụng hợp lý các sơ khai và tiêm phụ thuộc, thay vì dựa vào cái nạng của Đồ giả và đồ giả

  2. Giả mạo và mô phỏng rất hữu ích cho việc kiểm tra mã mà bạn không nên hoặc không thể thay đổi, chẳng hạn như:

    • Mã kế thừa không sử dụng (hoặc sử dụng hiệu quả) các mã sơ khai
    • API của bên thứ 3
    • Tài nguyên mà bạn không có mã nguồn

Shims (được gọi là "Nốt ruồi", trong quá trình phát triển) thực sự là một khuôn khổ chế giễu hoạt động bằng cách tách các lệnh gọi. Thay vì chăm chút xây dựng một mô hình (vâng, thậm chí sử dụng Moq cũng tương đối khó khăn!), Shims chỉ cần sử dụng đối tượng mã sản xuất đã có sẵn. Shims chỉ cần định tuyến lại cuộc gọi từ mục tiêu sản xuất đến đại biểu thử nghiệm.

Stubs được tạo từ các giao diện trong dự án mục tiêu. Đối tượng Stub chỉ là vậy - một phần triển khai của giao diện. Lợi ích của việc sử dụng loại Stub là bạn có thể nhanh chóng tạo ra một sơ khai mà không làm xáo trộn dự án thử nghiệm của mình với nhiều sơ khai sử dụng một lần, chưa kể đến việc lãng phí thời gian tạo chúng. Tất nhiên, bạn vẫn nên tạo các bản gốc cụ thể để sử dụng trong nhiều thử nghiệm.

Việc triển khai hiệu quả Fakes (các loại Shims, Mocks và Stub) sẽ mất một chút thời gian để làm quen, nhưng rất đáng để nỗ lực. Cá nhân tôi đã tiết kiệm hàng tuần thời gian phát triển, thông qua việc sử dụng các loại Shims / Mole, Mocks và Stub. Tôi hy vọng bạn có nhiều niềm vui với công nghệ như tôi có!


11
Đã phản đối vì thiếu thông tin cụ thể và một số vấn đề về thuật ngữ với nhận xét của bạn về Hàng giả. Giả mạo là một thuật ngữ chung cho tất cả các loại bài kiểm tra đôi và cũng là tên MS được sử dụng cho thư viện hàng giả của chúng. Trong thư viện của họ chỉ có Shims thực sự là Nốt ruồi, còn cuống thì không. Về sự cần thiết của các ví dụ, tôi muốn xem một ví dụ trong câu trả lời của bạn cho thấy cách tạo một miếng đệm (hoặc sơ khai) bằng Fakes đơn giản hơn so với việc sử dụng Moq. Nếu bạn thay đổi câu trả lời của mình để giải quyết những vấn đề này, tôi sẽ thay đổi phiếu phản đối của mình.
Jim Cooper

1
Nhưng có thêm lý do chính đáng cho việc sử dụng miếng chêm (nốt ruồi), tức là mã của bên thứ 3, API hệ thống / SDK. Nó không chỉ là khi bạn đang làm việc với các giải pháp nội bộ của riêng bạn. Vì vậy, tôi sẽ lên bạn một phiếu bầu cho ra thậm chí là :-)
Tony Tường

2
@Mike và Jim .. Tôi đã cố gắng sửa thuật ngữ được sử dụng trong câu trả lời này. Vui lòng cho tôi biết nếu chúng tôi có thể làm cho nó tốt hơn. Cảm ơn.
Snesh

15

Theo tôi hiểu, nhóm Visual Studio muốn tránh cạnh tranh với các thư viện giả khác nhau có sẵn cho .NET. MS thường phải đối mặt với những quyết định khó khăn như thế này. Họ thật đáng nguyền rủa nếu họ không cung cấp một số chức năng nhất định ("tại sao MS không cung cấp cho chúng tôi một thư viện mô phỏng; mô phỏng là một yêu cầu phổ biến như vậy?") Và chết tiệt nếu họ làm vậy ("tại sao Microsoft lại hành động mạnh mẽ và thúc đẩy những người ủng hộ tự nhiên ngoài thị trường? ") Rất thường xuyên, nhưng không phải lúc nào, họ quyết định từ chối chỉ cung cấp sự thay thế của riêng họ cho các công nghệ có sẵn và được đón nhận. Đó dường như là trường hợp ở đây.

Tính năng miếng đệm của Fakes thực sự rất hữu ích. Chắc chắn, có những nguy hiểm. Cần có một số kỷ luật để đảm bảo bạn chỉ sử dụng nó khi cần thiết. Tuy nhiên, nó lấp đầy một khoảng trống lớn. Khiếu nại chính của tôi là nó chỉ được cung cấp với phiên bản Ultimate của VS 2012 và do đó sẽ chỉ có sẵn cho một phần phụ của cộng đồng phát triển .NET. Thật đáng tiếc.


2
Khung làm giả cũng có sẵn với phiên bản Premium.
AlwaysAProgrammer,

13

Hàng giả bao gồm hai loại đối tượng "giả" khác nhau. Đầu tiên, được gọi là "sơ khai", về cơ bản là một hình nộm được tạo tự động có hành vi mặc định có thể (và thường sẽ) bị ghi đè để biến nó thành một mô phỏng thú vị hơn. Tuy nhiên, nó thiếu một số tính năng mà hầu hết các khung chế tạo hiện có sẵn cung cấp. Ví dụ: nếu bạn muốn kiểm tra xem một phương thức trên một cá thể sơ khai đã được gọi hay chưa, bạn cần phải tự thêm logic cho điều này. Về cơ bản, nếu bây giờ bạn đang tạo ra các bản chế của riêng mình theo cách thủ công, thì phần sơ khai có thể giống như một sự cải tiến. Tuy nhiên, nếu bạn đang sử dụng một khuôn khổ chế tạo đầy đủ tính năng hơn, bạn có thể cảm thấy như thiếu một số phần quan trọng trong sơ khai của Fakes.

Loại đối tượng khác do Fakes đưa ra, được gọi là "miếng đệm", cho thấy cơ chế thay thế hành vi của các phần phụ thuộc chưa được (hoặc không thể) tách ra đủ để thay thế tiêu chuẩn thông qua mocks. AFAIK, TypeMock là một trong những khung chế tạo chính duy nhất hiện cung cấp loại chức năng này.

BTW, nếu bạn đã thử Moles trước đây, Fakes về cơ bản cũng giống như vậy, cuối cùng đã thoát khỏi Microsoft Research và trở thành một sản phẩm thực tế.


1
Cập nhật: Nốt ruồi hiện đã được tích hợp vào MS Fakes. "Fakes Framework trong Visual Studio 2012 là thế hệ tiếp theo của Moles & Stubs. Tuy nhiên, Fakes khác với Moles, vì vậy việc chuyển từ Moles sang Fakes sẽ yêu cầu một số sửa đổi đối với mã của bạn. Khung Moles sẽ không được hỗ trợ trong Visual Studio 2012 . " Nguồn: research.microsoft.com/en-us/projects/moles
Sire

1

Về đối tượng Fake (Shim + Stub), nó đã được xác định rõ ở trên, mặc dù tôi đoán rằng đoạn cuối trong bình luận cuối cùng tóm tắt toàn bộ tình huống khá tốt.

Mặc dù nhiều người sẽ tranh luận rằng các đối tượng Fake (Shim + Stub) là nội dung tốt để có trong một số trường hợp kiểm thử đơn vị, nhưng nhược điểm là bất kể bạn đang sử dụng Visual Studio 2012 hay Visual Studio 2013, các tùy chọn này CHỈ khả dụng với các phiên bản Premium hoặc Ultimate. IOW, điều này có nghĩa là bạn SẼ KHÔNG chạy BẤT KỲ trong số Fakes (Shim + Stub) đó trên bất kỳ phiên bản Pro nào.

Bạn có thể thấy tùy chọn menu Giả mạo (Shim + Stub) trên các phiên bản Pro, nhưng hãy cẩn thận, có một số khả năng khá cao là bạn sẽ hoàn toàn không có gì cả ... Nó sẽ không tạo ra bất kỳ lỗi biên dịch nào cho bạn biết rằng điều gì đó quan trọng bị thiếu, các tùy chọn không có ở đó, vì vậy đừng lãng phí thời gian của bạn ...

Đó là một yếu tố quan trọng cần xem xét trong một nhóm phát triển, đặc biệt nếu một người là người duy nhất sử dụng phiên bản Ultimate trong khi mọi người khác sử dụng phiên bản Pro ... Mặt khác, Moq có thể dễ dàng được cài đặt thông qua Nuget cho dù bạn sử dụng phiên bản Visual Studio nào. Tôi không gặp vấn đề gì khi sử dụng Moq, chìa khóa với bất kỳ công cụ nào là biết chúng được sử dụng để làm gì và cách sử dụng chúng đúng cách;)


1
Vui lòng định dạng câu hỏi của bạn thành các đoạn văn nhỏ hơn để dễ đọc hơn.

@SirXamelot, không refactoring mã bạn không thể thay đổi kết quả của .net cung cấp các cuộc gọi tĩnh ví dụ DateTime.Now hoặc Guid.NewGuid
zaitsman
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.