2010-09-24 40 views
11

Phương pháp mở rộng không tốt để thử nghiệm (được mô tả ở đây: Mocking Extension Methods with Moq, http://www.clariusconsulting.net/blogs/kzu/archive/2009/12/22/Howtomockextensionmethods.aspx).Làm thế nào để Mock (với Moq) Phương pháp Unity

Nhưng có lẽ có một số giải pháp để chế nhạo các phương pháp Unity? Trong trường hợp của tôi, tôi có chức năng sau:

public class MyManager 
{ 
    public MyManager(IUnityContainer container) : base(container) { } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = MyContainer.Resolve<IMyLog>(); 

     ... use log.Id ... 

     MyContainer.Resolve<...>();//usage for other purposes... 
    } 

Tôi muốn chắc chắn rằng phương pháp 'DoJob' sẽ luôn có được đối tượng 'IMyLog' từ container, nhưng không phải từ các nguồn khác ... làm thế nào tôi có thể kiểm tra điều đó không?

ý tưởng ban đầu của tôi là thay đổi 'DoJob' thực hiện phương pháp và sử dụng:

IMyLog log = UnityContainer.Resolve(typeof(IMyLog)) as IMyLog; 

Nhưng 'Resolve (Type t, ...) cũng là một phương pháp khuyến nông ...

Bất kỳ suy nghĩ được hoan nghênh.

P.S. Xin lưu ý rằng đối tượng 'đăng nhập của tôi' được tạo xa từ MyManager.DoJob ...

+1

Phụ thuộc vào thùng chứa IoC là một mẫu giả. Cố gắng hạn chế vùng chứa ở một cấp cao nhất và tạo nó cho các đối tượng khác của bạn. Như @ TheCodeKing đề xuất, các nhà máy tự động có thể trợ giúp. Xem http://kozmic.pl/2010/06/20/how-i-use-inversion-of-control-containers/ – TrueWill

Trả lời

3

Guess, tôi thấy giải pháp thích hợp nhất cho bài kiểm tra: Nó không phải là cần thiết để chứa thống nhất mô hình và kiểm tra xem ' đối tượng log 'đã được lấy từ nó. Tôi sẽ chỉ thực hiện một mô hình cho đối tượng 'Đăng nhập', đăng ký đối tượng của nó trong thùng chứa và kiểm tra trong thử nghiệm nếu đối tượng đăng nhập này thực sự được sử dụng.

Điều này sẽ làm những gì được yêu cầu.

 Mock<IMyLog> mockLog = new Mock<IMyLog>(); 
     mockLog.Setup(mock=>mock.Id).Returns(TestLogId); 

     IUnityContainer container = new UnityContainer(); 
     container 
      .RegisterInstance(mockCommandExecutionLog.Object) 
      ... 
      ; 

     ... 

     mockLog.Verify(
      mock => mock.Id, 
      Times.Once(), 
      "It seems like 'Log' object is not used" 
      ); 

Cảm ơn.

6

Xóa phụ thuộc vào IUnityContainer và mọi thứ trở nên dễ dàng hơn và sạch hơn nhiều. Thay vào đó hãy để cho sự thống nhất đưa các phụ thuộc của bạn được trừu tượng hóa vào các giao diện. Đây là một cách dễ dàng chế nhạo. Dưới đây là một ví dụ bằng cách sử dụng một mẹo nhỏ với Unity để chèn một nhà máy tự động cho IMyLog.

public class MyManager 
{ 
    private readonly Func<IMyLog> logFactory; 

    public MyManager(Func<IMyLog> logFactory) 
    { 
     this.logFactory = logFactory; 
    } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = logFactory(); 

     ... 
    } 
} 

Hoặc nếu bạn không cần phải tạo ra các ví dụ mỗi lần:

public class MyManager 
{ 
    private readonly IMyLog myLog; 

    public MyManager(IMyLog myLog) 
    { 
     this.myLog = myLog; 
    } 

    public IResult DoJob(IData data) 
    { 
     ... 
    } 
} 
+0

Trong trường hợp của tôi, tôi cần Unity cho các mục đích khác ... (bài đăng gốc đã được cập nhật gần đây) . Tôi có lẽ có thể trích xuất không phải là một đăng nhập nhưng một nhà máy đăng nhập và kiểm tra nếu nó 'GetLogObject' phương pháp được gọi là ... Cảm ơn ý tưởng. – Budda

1

Tôi sẽ phải không đồng ý với cả hai câu trả lời. TheCodeKing, hoàn toàn hợp pháp để sử dụng giao diện IoC trực tiếp. Một ví dụ có thể là một nhà máy điều khiển trong dự án ASP.NET - nơi mà một người có thể thực hiện độ phân giải không tầm thường bằng nhiều phương thức trên giao diện IUnityContainer.

Hmm ... với labdas tiêm tự động tại nhà máy, người ta không bao giờ phải kiểm tra trực tiếp giao diện IoC. Bạn chỉ có thể vượt qua một lambda và xác minh rằng nó được gọi với các tham số đúng.

Bạn, bạn nên không bao giờ mang theo một thùng chứa IoC vào thử nghiệm đơn vị của bạn. Các phụ thuộc phải được tiêm thủ công.

Dưới đây là giải pháp tôi sử dụng. Về cơ bản, tôi đã tạo một lớp trừu tượng trên IUnityContainer và triển khai một lớp đơn giản ủy quyền cho IUnityContainer. Bởi vì giao diện của tôi không chứa các phương thức mở rộng, tôi có thể dễ dàng giả lập nó.

public interface IDIContainer { 
    void RegisterType<TFrom>() where TFrom : class; 
    void RegisterType<TFrom, TTo>() where TTo : TFrom; 
    void RegisterType<TFrom, TTo>(string name) where TTo : TFrom; 
    void RegisterType(Type from, Type to); 
    void RegisterType(Type from, Type to, string name); 

    void RegisterInstance<TFrom>(TFrom instance) where TFrom : class; 

    T Resolve<T>(); 
    T Resolve<T>(string name); 
    IEnumerable<T> ResolveAll<T>(); 

    bool IsRegistered<TFrom>(string name) where TFrom : class; 
    bool IsRegistered<TFrom>() where TFrom : class; 
} 


public class DIContainer : IDIContainer { 
    IUnityContainer m_Container = new UnityContainer(); 

    #region IDIContainer Members 

    public void RegisterType<TFrom>() where TFrom : class { 
     m_Container.RegisterType<TFrom>(); 
    } 

    public void RegisterType<TFrom, TTo>() where TTo : TFrom { 
     m_Container.RegisterType<TFrom, TTo>(); 
    } 

    public void RegisterType<TFrom, TTo>(string name) where TTo : TFrom { 
     m_Container.RegisterType<TFrom, TTo>(name); 
    } 

    public void RegisterType(Type from, Type to) { 
     m_Container.RegisterType(from, to); 
    } 

    public void RegisterType(Type from, Type to, string name) { 
     m_Container.RegisterType(from, to, name); 
    } 

    public void RegisterInstance<TFrom>(TFrom instance) where TFrom : class { 
     m_Container.RegisterInstance<TFrom>(instance); 
    } 

    public T Resolve<T>() { 
     return m_Container.Resolve<T>(); 
    } 

    public IEnumerable<T> ResolveAll<T>() { 
     return m_Container.ResolveAll<T>(); 
    } 

    public T Resolve<T>(string name) { 
     return m_Container.Resolve<T>(name); 
    } 

    public bool IsRegistered<TFrom>(string name) where TFrom : class { 
     return m_Container.IsRegistered<TFrom>(name); 
    } 

    public bool IsRegistered<TFrom>() where TFrom : class { 
     return m_Container.IsRegistered<TFrom>(); 
    } 

    #endregion 
} 

Bây giờ, viết lại lớp học của bạn để sử dụng IDIContainer:

public class MyManager 
{ 
    public MyManager(IDIContainer container) : base(container) { } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = MyContainer.Resolve<IMyLog>(); 

     ... use log.Id ... 

     MyContainer.Resolve<...>();//usage for other purposes... 
    } 
} 

Và viết lại bài kiểm tra đơn vị như sau:

[TestClass] 
public class Test { 
    [TestMethod] 
    public void TestDoJob() { 
    Mock<IMyLog> mockLog = new Mock<IMyLog>(); 
    Mock<IDIContainer> containerMock = new Mock<IDIContainer>(); 

    //Setup mock container to return a log mock we set up earlier 
    containerMock.Setup(c=>c.Resolve<IMyLog>()),Returns(mockLog); 
    //Verify that all setups have been performed 
    containerMock.VerifyAll(); 
    } 
} 
+0

Tôi đã không nói rằng nó không hợp pháp để sử dụng IoC, chỉ là nó đã làm cho nó dễ dàng hơn để kiểm tra với phụ thuộc trực tiếp. Kịch bản của nhà máy đã thực sự được sử dụng trong ví dụ của tôi. Nó có thể đạt được bằng cách sử dụng tính năng auto-factory của Unity mà không cần tham chiếu đến IoC. – TheCodeKing

+0

Igor, cảm ơn, đó là một giải pháp thực sự tốt. Cảm ơn! – Budda

+0

@TheCodeKing Chỉ cần đọc lại bài đăng của bạn. Tôi không biết về hỗ trợ nhà máy tự động. Cùng với 'InjectionFactory', bạn có thể cần phải tiêm container. Tôi lấy lại tất cả :) –

6

Tôi biết tôi là muộn để đảng, nhưng tôi có cùng một vấn đề và giải quyết vấn đề bằng cách chế nhạo phương thức sau -

IUnityContainer.Resolve(Type t, string name, params ResolverOverride[] resolverOverrides); 

Ví dụ -

unityMock = new Mock<IUnityContainer>(MockBehavior.Strict); 
validatorMock = new Mock<IValidator>(MockBehavior.Strict); 
unityMock.Setup(p => p.Resolve(typeof(IValidator), null)) 
    .Returns(validatorMock.Object); 

Chỉ trong trường hợp ai đó cần phải thử này ra và không thể loại bỏ phụ thuộc trên thùng sơn.

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