16

Sau tất cả những gì tôi đã đọc về Dependency Injection và IoC, tôi đã quyết định thử sử dụng Windsor Container trong ứng dụng của chúng tôi (đó là ứng dụng web nhiều lớp 50K LOC, vì vậy tôi hy vọng nó không phải là quá mức cần thiết). Tôi đã sử dụng một lớp tĩnh đơn giản để gói các container và tôi khởi tạo nó khi bắt đầu ứng dụng, mà hoạt động khá tốt cho bây giờ.Chiến lược kiểm thử đơn vị khi sử dụng IoC là gì?

Câu hỏi của tôi là về thử nghiệm đơn vị. Tôi biết rằng DI sẽ làm cho cuộc sống của tôi dễ dàng hơn nhiều ở đó bằng cách cho tôi khả năng tiêm các triển khai sơ khai/giả lập của các cộng tác viên lớp cho lớp đang được kiểm tra. Tôi đã viết một vài bài kiểm tra bằng cách sử dụng kỹ thuật này và nó có vẻ hợp lý với tôi. Những gì tôi không chắc chắn là liệu tôi có nên sử dụng IoC (trong trường hợp này là Windsor Castle) hay không trong các bài kiểm tra đơn vị (có thể bằng cách nào đó cấu hình nó để trả lại sơ khai/mocks cho các trường hợp đặc biệt của tôi) hoặc tốt hơn để kết nối tất cả các phụ thuộc thủ công trong các bài kiểm tra. Bạn nghĩ gì và thực hành nào đã làm việc cho bạn?

+2

Trùng lặp: http://stackoverflow.com/questions/1465849/using-ioc-for-unittesting –

+0

Cảm ơn, tôi không thể tìm thấy nó ở bất cứ đâu;) –

Trả lời

20

Bạn không cần DI trong các bài kiểm tra đơn vị vì phụ thuộc được cung cấp thông qua các đối tượng giả được tạo bằng các khung như Rhino Mocks hoặc Moq. Vì vậy, ví dụ khi bạn đang thử nghiệm một lớp có một sự phụ thuộc vào một số giao diện, sự phụ thuộc này thường được cung cấp thông qua quá trình xây dựng.

public class SomeClassToTest 
{ 
    private readonly ISomeDependentObject _dep; 
    public SomeClassToTest(ISomeDependentObject dep) 
    { 
     _dep = dep; 
    } 

    public int SomeMethodToTest() 
    { 
     return _dep.Method1() + _dep.Method2(); 
    } 
} 

Trong ứng dụng của bạn, bạn sẽ sử dụng một khuôn khổ DI để vượt qua một số thực hiện thực sự của ISomeDependentObject trong constructor mà tự nó có thể có phụ thuộc vào các đối tượng khác trong khi trong một thử nghiệm đơn vị bạn tạo một đối tượng giả bởi vì bạn chỉ muốn kiểm tra lớp này trong sự cô lập. Ví dụ với Rhino Mocks:

[TestMethod] 
public void SomeClassToTest_SomeMethodToTest() 
{ 
    // arrange 
    var depStub = MockRepository.CreateStub<ISomeDependentObject>(); 
    var sut = new SomeClassToTest(depStub); 
    depStub.Stub(x => x.Method1()).Return(1); 
    depStub.Stub(x => x.Method2()).Return(2); 

    // act 
    var actual = sut.SomeMethodToTest(); 

    // assert 
    Assert.AreEqual(3, actual); 
} 
+0

Âm thanh hợp lý. Sau khi tất cả, điều này là chính xác những gì tôi đang làm. Tôi đã không chắc chắn cho dù đó là một thực hành tốt, đặc biệt là khi có rất nhiều phụ thuộc và tôi phải tự tay giả tất cả. –

+0

Hãy nhìn vào automocking tôi đã nói về câu trả lời của tôi Thomas, nó thực sự đơn giản hóa các lớp thử nghiệm với rất nhiều phụ thuộc. :-) –

2

Tôi vừa viết một phong cách và ứng dụng kích thước rất giống nhau. Tôi sẽ không đưa bất kỳ sự phụ thuộc nào vào các bài kiểm tra đơn vị vì nó không đủ phức tạp để cần thiết. Bạn nên sử dụng một khung mocking để tạo mocks của bạn (RhinoMocks/Moq).

Ngoài ra Automocking trong Moq hoặc Auto Mock Container trong Rhinomocks sẽ đơn giản hóa việc xây dựng mô hình của bạn hơn nữa.

Tự động mô phỏng cho phép bạn lấy đối tượng loại bạn muốn kiểm tra mà không cần thiết lập mocks bằng tay. Tất cả các phụ kiện được tự động chế nhạo (giả sử chúng là các giao diện) và được tiêm vào bộ tạo kiểu. Nếu bạn cần phải thiết lập hành vi dự kiến, nhưng bạn không phải làm như vậy.

+0

Có, nhưng không phải Auto Mock Container cũng là một container IoC? ;) Nó chỉ là nó thoải mái hơn về giải quyết phụ thuộc: nó sẽ tạo ra mặc định thực hiện giả của dịch vụ mà không ném ngoại lệ cho các công cụ chưa được giải quyết như "đúng" Windsor container nào. –

+1

... và quan trọng nhất là chỉ mất hai dòng để tạo! Thực tế là bạn không cần phải đăng ký bất kỳ phụ thuộc có nghĩa là nó tải dễ dàng hơn một IoC thường xuyên. Một lý do khác để sử dụng automocking mặc dù là trừu tượng đi các cuộc gọi đến đối tượng theo constructor của thử nghiệm. Vì vậy, nếu chữ ký thay đổi bạn không phải thay đổi nhiều bài kiểm tra, làm cho các lớp thử nghiệm của bạn trở nên ít giòn hơn. :-) –

3

Tôi đang làm việc trên dự án ASP.NET MVC với khoảng 400 bài kiểm tra đơn vị. Tôi đang sử dụng Ninject cho tiêm phụ thuộc và MBUnit để thử nghiệm.

Ninject không thực sự cần thiết cho thử nghiệm đơn vị, nhưng nó làm giảm số lượng mã tôi phải nhập. Tôi chỉ phải chỉ định một lần (mỗi dự án) cách giao diện của tôi nên được khởi tạo như trái ngược với việc này mỗi lần tôi khởi tạo lớp đang được thử nghiệm.

Để tiết kiệm thời gian viết các bài kiểm tra mới, tôi đã tạo các lớp cơ sở thử nghiệm cố định với nhiều mã thiết lập chung nhất có thể. Các thủ tục thiết lập trong các lớp đó sẽ khoanh vùng các kho lưu trữ giả, tạo ra một số dữ liệu thử nghiệm và một nhận dạng giả cho người dùng thử nghiệm. Kiểm tra đơn vị chỉ khởi tạo dữ liệu quá cụ thể để đi vào quy trình thiết lập chung.

Tôi cũng đang chế nhạo các đối tượng (trái ngược với giả mạo) trong một số thử nghiệm, nhưng tôi thấy rằng các kho dữ liệu giả mạo dẫn đến ít công việc hơn và kiểm tra chính xác hơn. Ví dụ, nó sẽ khó khăn hơn để kiểm tra nếu phương pháp được kiểm tra tôi đúng cam kết tất cả các bản cập nhật cho kho lưu trữ sau khi thực hiện chúng khi sử dụng một kho lưu trữ giả hơn khi tôi đang sử dụng một kho giả mạo.

Đó là khá nhiều công việc để thiết lập ngay từ đầu, nhưng thực sự đã giúp tôi giảm tiết kiệm rất nhiều thời gian về lâu dài.

0

Như Darin đã chỉ ra, bạn không cần sử dụng DI nếu bạn có mocks. (Tuy nhiên, DI cũng có một số lợi ích khác, bao gồm, trước tiên, giảm bớt các phụ thuộc trong mã của bạn, điều này làm cho mã của bạn dễ bảo trì và mở rộng trong thời gian dài hơn.)

Cá nhân tôi thích kết nối mọi thứ hơn trong các bài kiểm tra đơn vị của tôi, do đó dựa vào ít nhất có thể trên các khung bên ngoài, các tệp cấu hình, v.v.

Các vấn đề liên quan