Làm cách nào để chế nhạo một lớp không có giao diện?


81

Tôi đang làm việc trên .NET 4.0 bằng C # trong Windows 7.

Tôi muốn kiểm tra giao tiếp giữa một số phương pháp bằng cách sử dụng mô hình. Vấn đề duy nhất là tôi muốn làm điều đó mà không cần triển khai giao diện. Điều đó có thể không?

Tôi vừa đọc rất nhiều chủ đề và một số hướng dẫn về các đối tượng giả, nhưng tất cả chúng đều dùng để mô phỏng các giao diện chứ không phải các lớp học. Tôi đã cố gắng sử dụng các khung công tác Rhino và Moq.


1
Thực sự đáng tiếc rằng những công cụ này được tạo ra từ quan điểm sử dụng độc quyền "IInterfaces".
AR

4
Có giả định rằng bạn đang sử dụng DI dựa trên giao diện. Đây là một mẫu khá chuẩn ngày nay.
Maess

Thật không may, mô hình đó xung đột với "mô hình" Loại bất biến :(
Matthew Watson

3
Có rất nhiều phương pháp không đề cập ở đây trong câu trả lời này
BlueRaja - Danny Pflughoeft

Câu trả lời:


65

Chỉ cần đánh dấu bất kỳ phương thức nào bạn cần để giả mạo là virtual(và không phải là riêng tư). Sau đó, bạn sẽ có thể tạo giả mạo có thể ghi đè phương thức.

Nếu bạn sử dụng new Mock<Type>và bạn không có hàm tạo không tham số thì bạn có thể chuyển các tham số làm đối số của lệnh gọi trên vì nó có kiểuparam Objects


1
Điều này khiến tôi tự hỏi liệu có cần thiết phải tạo giao diện cho mọi lớp mà tôi muốn mô phỏng hay không. Chúng ta không thể chỉ sử dụng các lớp cụ thể để chế nhạo nếu chúng không có giao diện?
orad

13
@orad Trên thực tế, tôi có xu hướng tạo một lớp trước, chỉ tạo một giao diện nếu tôi cần chia nhỏ chức năng chung.
Justin Pihony

Làm thế nào để bạn chuyển mô hình cho một đối tượng mong đợi kiểu được chỉ định? Nếu tôi tạo một mô hình 'Mô hình mới <MyType>' và cố gắng chuyển nó cho một đối tượng mong đợi MyType, tôi sẽ gặp lỗi "Không thể chuyển đổi Mock <MyType> thành MyType".
Neutrino

2
Tôi đã giải quyết vấn đề đó, nếu bạn định nghĩa mô hình của mình là 'var myMock = new Mock <MyType> ()', bạn phải chuyển nó cho lớp sử dụng mô hình đó là myMock.Object.
Neutrino

4
Vấn đề chính nảy sinh khi không có hàm tạo không tham số và hàm tạo tham số trong lớp Concrete đóng không công khai. (Ở đây, ý tôi là 'đóng' trong một gói bên ngoài). Sau đó, người ta không thể triển khai một giao diện, không thể làm cho phương thức đó ảo và cũng không thể sử dụng phương thức tạo tham số.
Abhilash Pokhriyal

22

Hầu hết các khuôn khổ chế nhạo (bao gồm Moq và RhinoMocks) đều tạo ra các lớp proxy để thay thế cho lớp bị chế nhạo của bạn và ghi đè các phương thức ảo bằng hành vi mà bạn xác định. Do đó, bạn chỉ có thể giả lập các giao diện hoặc các phương thức ảo trên các lớp cụ thể hoặc trừu tượng. Ngoài ra, nếu bạn đang chế nhạo một lớp cụ thể, bạn hầu như luôn cần cung cấp một phương thức khởi tạo không tham số để khuôn khổ chế tạo biết cách khởi tạo lớp đó.

Tại sao lại không thích tạo giao diện trong mã của bạn?


102
Bởi vì nó làm tắc nghẽn cơ sở mã với hàng tấn giao diện, nếu không phải vì một số giới hạn kỹ thuật của các khung thử nghiệm, sẽ hoàn toàn không cần thiết?
BlueRaja - Danny Pflughoeft

9
Bạn đã nghe thấy cụm từ "phạm vi tiếp cận vượt quá khả năng nắm bắt". Điều đó giải thích tại sao các nhà phát triển luôn phàn nàn. Ngôn ngữ C # không được thiết kế với mục đích kiểm tra đơn vị. Đó là lý do tại sao thực sự khó có thể đưa vào các phụ thuộc giả mà không có một mớ hỗn độn các giao diện vô nghĩa hoặc các phương thức ảo. Có lẽ ai đó sẽ sớm phát minh ra một ngôn ngữ dễ kiểm tra đơn vị. Đến lúc đó chúng ta sẽ có điều gì khác để phàn nàn.
John Henckel,

13
Tôi rất vui khi biết mình không phải là người duy nhất xem các giao diện và phương thức ảo là lộn xộn nếu mục đích duy nhất của chúng là phục vụ các thử nghiệm.
aaaaaa

13
"mục đích duy nhất là để phục vụ các thử nghiệm" nhưng việc tạo mã có thể thử nghiệm không quan trọng đối với bạn?
MakkyNZ

12
"Tại sao lại không thích việc tạo giao diện trong mã của bạn?" Bởi vì tôi không viết đối tượng này và tôi không có quyền truy cập vào mã. Tôi cần tạo một mô hình của một đối tượng đóng mà tôi không sở hữu mà không triển khai giao diện.
Sean Worle

16

Với MoQ, bạn có thể giả lập các lớp cụ thể:

var mocked = new Mock<MyConcreteClass>();

nhưng điều này cho phép bạn ghi đè virtualmã (phương thức và thuộc tính).


1
Tôi đã thử điều đó, nhưng khi tôi chạy dự án thử nghiệm của mình, chương trình của tôi ném ra một ngoại lệ: "Không thể khởi tạo proxy của lớp" "Không thể tìm thấy một hàm tạo không tham số."
Vinicius Seganfredo

2
Chỉ cần truyền các tham số của phương thức khởi tạo đến phương thức khởi tạo Mock <>. Ví dụnew Mock<MyConcreteClass>(param1, anotherParam, thirdParam, evenMoreParams);
Bozhidar Stoyneff,

1
Tại sao không chỉ tạo một giao diện cho lớp?
Robert Perry

11

Tôi nghĩ tốt hơn nên tạo giao diện cho lớp đó. Và tạo một bài kiểm tra đơn vị bằng giao diện.

Nếu bạn không có quyền truy cập vào lớp đó, bạn có thể tạo bộ điều hợp cho lớp đó.

Ví dụ:

public class RealClass
{
    int DoSomething(string input)
    {
        // real implementation here
    }
}

public interface IRealClassAdapter
{
    int DoSomething(string input);
}

public class RealClassAdapter : IRealClassAdapter
{
    readonly RealClass _realClass;

    public RealClassAdapter() => _realClass = new RealClass();

    int DoSomething(string input) => _realClass.DoSomething(input);
}

Bằng cách này, bạn có thể dễ dàng tạo mô hình cho lớp của mình bằng IRealClassAdapter.

Hy vọng nó hoạt động.


4
Điều này là khủng khiếp cho việc bảo trì. Nếu bạn muốn thêm một phương thức mới vào RealClass, bạn cũng phải thêm nó vào IReadClassAdapter và cả RealClassAdapter. Nỗ lực TRIPLE! Giải pháp tốt hơn là chỉ cần thêm từ khóa "virtual" vào mỗi phương thức công khai trong RealClass.
John Henckel,

12
@JohnHenckel Tôi giả định rằng chúng tôi không có quyền truy cập vào RealClass. Vì vậy, thêm phương pháp "ảo" không phải là một lựa chọn theo quan điểm của tôi. Nếu bạn có quyền truy cập vào lớp thực thì tốt hơn nên triển khai giao diện trực tiếp cho lớp mới, tôi nghĩ đó là cách tốt nhất.
Anang Satria

Cảm ơn. Đây dường như là giải pháp hợp pháp để chế giễu các lớp không có quyền truy cập. Ngay cả khi đó là rất nhiều mã "ngu ngốc", tôi cần điều này để chạy mã trong "môi trường không thích hợp"
Michael Spannbauer

7

Các khung chế tạo tiêu chuẩn đang tạo ra các lớp proxy. Đây là lý do tại sao chúng bị giới hạn về mặt kỹ thuật đối với các giao diện và phương thức ảo.

Nếu bạn cũng muốn bắt chước các phương pháp 'bình thường', bạn cần một công cụ hoạt động với thiết bị đo đạc thay vì tạo proxy. Ví dụ: MS Moles và Typemock có thể làm điều đó. Nhưng cái trước có 'API' khủng khiếp và cái sau là thương mại.


chúng ta có thể giả lập lớp bình thường với phương thức công khai đó không?
SivaRajini


2

Nếu tình trạng tệ hơn trở nên tồi tệ hơn, bạn có thể tạo một cặp giao diện và bộ điều hợp. Bạn sẽ thay đổi tất cả các cách sử dụng ConcreteClass để sử dụng giao diện thay thế và luôn chuyển bộ điều hợp thay vì lớp cụ thể trong mã sản xuất.

Bộ điều hợp triển khai giao diện, vì vậy mô hình cũng có thể triển khai giao diện.

Nó giống như một giàn giáo hơn là chỉ tạo một phương thức ảo hoặc chỉ thêm một giao diện, nhưng nếu bạn không có quyền truy cập vào nguồn cho lớp cụ thể, nó có thể giúp bạn thoát khỏi ràng buộc.


1

Tôi đã phải đối mặt với điều gì đó tương tự như vậy trong một trong những dự án cũ và kế thừa mà tôi đã làm việc trong đó không chứa bất kỳ giao diện hoặc phương pháp hay nhất nào và cũng quá khó để thực thi chúng xây dựng lại mọi thứ hoặc cấu trúc lại mã do sự trưởng thành của kinh doanh dự án, Vì vậy trong dự án UnitTest của tôi, tôi đã sử dụng để tạo một Wrapper trên các lớp mà tôi muốn mô phỏng và giao diện triển khai wrapper đó chứa tất cả các phương thức cần thiết của tôi mà tôi muốn thiết lập và làm việc, Bây giờ tôi có thể giả lập wrapper thay vì lớp thực.

Ví dụ:

Dịch vụ bạn muốn kiểm tra không chứa các phương thức ảo hoặc giao diện triển khai

public class ServiceA{

public void A(){}

public String B(){}

}

Wrapper thành moq

public class ServiceAWrapper : IServiceAWrapper{

public void A(){}

public String B(){}

}

Giao diện Wrapper

public interface IServiceAWrapper{

void A();

String B();

}

Trong bài kiểm tra đơn vị, bây giờ bạn có thể giả lập trình bao bọc:

    public void A_Run_ChangeStateOfX()
    {
    var moq = new Mock<IServiceAWrapper>();
    moq.Setup(...);
    }

Đây có thể không phải là phương pháp hay nhất, nhưng nếu các quy tắc dự án của bạn buộc bạn theo cách này, hãy làm điều đó. Đồng thời Đặt tất cả các Trình bao bọc của bạn bên trong dự án Kiểm tra đơn vị hoặc dự án Người trợ giúp chỉ được chỉ định cho các thử nghiệm đơn vị để không làm quá tải dự án với các trình bao bọc hoặc bộ điều hợp không cần thiết.

Cập nhật: Câu trả lời này từ hơn một năm nhưng trong năm nay, tôi phải đối mặt với rất nhiều tình huống tương tự với các giải pháp khác nhau. Ví dụ: thật dễ dàng để sử dụng Microsoft Fake Framework để tạo ra các bản giả, hàng giả và sơ khai và thậm chí kiểm tra các phương pháp riêng tư và được bảo vệ mà không cần bất kỳ giao diện nào. Bạn có thể đọc: https://docs.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes?view=vs-2017

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.