代码之家  ›  专栏  ›  技术社区  ›  Budda

如何(用Moq)模拟Unity方法

  •  13
  • Budda  · 技术社区  · 15 年前

    扩展方法不适合测试(这里描述: Mocking Extension Methods with Moq http://www.clariusconsulting.net/blogs/kzu/archive/2009/12/22/Howtomockextensionmethods.aspx ).

    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...
        }
    

    我想确保“DoJob”方法总是从容器中获取“IMyLog”对象,而不是从其他源获取。。。我怎么能测试呢?

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

    但是'Resolve(Type t,…)'也是一个扩展方法。。。

    欢迎有任何想法。

    请注意,“我的日志”对象的创建位置远离我的经理.DoJob...

    4 回复  |  直到 8 年前
        1
  •  9
  •   Jamie Burns    11 年前

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

    例如-

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

        2
  •  6
  •   TheCodeKing    15 年前

    去掉对IUnityContainer的依赖,事情变得更简单、更干净。相反,让unity注入抽象到接口中的依赖项。这些很容易被嘲笑。下面是一个使用Unity的小技巧为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();
    
           ...
       }
    }
    

    或者如果不需要每次都创建实例:

    public class MyManager
    {
       private readonly IMyLog myLog;
    
       public MyManager(IMyLog myLog) 
       {
           this.myLog = myLog;
       }
    
       public IResult DoJob(IData data)
       {
           ...
       }
    }
    
        3
  •  4
  •   Budda    15 年前

    这将完成所需的工作。

            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"
                );
    

    谢谢。

        4
  •  1
  •   Igor Zevaka    15 年前

    两个答案我都不同意。在代码方面,直接使用IoC接口是完全合法的。一个例子可能是一个控制器工厂ASP.NET项目-其中一个可以使用多个方法对 IUnityContainer

    隐马尔可夫模型。。。有了自动工厂注入的labdas,就不必直接对IoC接口进行单元测试了。您只需传递lambda并验证是否使用正确的参数调用它。

    巴达,你应该 在单元测试中引入一个IoC容器。依赖项必须手动注入。

    下面是我使用的解决方案。我基本上创建了一个抽象层 IUnity容器 实现了一个简单的类 . 因为我的接口不包含扩展方法,所以我可以轻松地模拟它。

    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
    }
    

    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...
        }
    }
    

    重写单元测试如下:

    [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();
      }
    }
    
    推荐文章