代码之家  ›  专栏  ›  技术社区  ›  Lews Therin

如果不控制代码,如何模拟一个具体类的依赖关系?

  •  0
  • Lews Therin  · 技术社区  · 5 年前

    我正在实施 IHttpModule 我正在尝试为它编写单元测试(使用nunit和moq)。我很难嘲笑 HttpApplication 依赖于 Init 方法:

    void Init(HttpApplication context);
    

    通常,asp.net控制 HTT应用程序 实例并将其传递给 初始化 方法。在 初始化 方法,自定义 IHTTP模块 订阅由 HTT应用程序 实例(如 BeginRequest EndRequest )

    我需要一些方法来嘲笑 HTT应用程序 所以我可以引发事件并测试 IHTTP模块 事件处理程序工作。

    我试着做一个模拟 HTT应用程序 在我的测试中:

    // Mock for the logging dependency
    var mockLogger = new Mock<ILogger>();
    
    // My attempt at mocking the HttpApplication
    var mockApplication = new Mock<HttpApplication>();
    
    // MyModule is my class that implements IHttpModule
    var myModule = new MyModule(mockLogger.Object);
    
    // Calling Init, which subscribes my event handlers to the HttpApplication events
    myModule.Init(mockApplication.Object);
    
    // Attempting to raise the begin and end request events
    mockApplication.Raise(a => a.BeginRequest += null, EventArgs.Empty);
    mockApplication.Raise(a => a.EndRequest += null, EventArgs.Empty);
    
    // RequestTime is a long property that tracks the time it took (in miliseconds) for a 
    // request to be processed and is set in the event handler subscribed to EndRequest
    Assert.Greater(myModule.RequestTime, 0);
    

    …但它给出了以下错误消息:

    表达式不是事件附加或分离,或者事件在类中声明但未标记为虚拟。

    当我研究这个错误时,我了解到moq只能模拟接口和虚拟方法…… 那么,我怎样才能模仿一个我无法控制的具体类呢?

    这里是 MyModule 班级:

    public class MyModule : IHttpModule
    {
        ILogger _logger;
    
        public long RequestTime { get; private set; }
    
        Stopwatch _stopwatch;
    
        public MyModule(ILogger logger)
        {
            _logger = logger;
        }
    
        public void Init(HttpApplication context)
        {
            context.BeginRequest += OnBeginRequest;
            context.EndRequest += OnEndRequest;
        }
    
        public void Dispose() { }
    
        void OnBeginRequest(object sender, EventArgs e)
        {
            _stopwatch = Stopwatch.StartNew();
        }
    
        void OnEndRequest(object sender, EventArgs e)
        {
            _stopwatch.Stop();
            RequestTime = _stopwatch.ElapsedMilliseconds;
        }
     }   
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Lews Therin    5 年前

    我认为通常不建议这样做,但是您可以使用反射来模拟非公共/虚拟成员。

    对于电流 implementation ,像这样

    public class MockHttpApplication : HttpApplication
    {
        public void RaiseBeginRequest()
        {
            FindEvent("EventBeginRequest").DynamicInvoke(this, EventArgs.Empty);
        }
    
        public void RaiseEndRequest()
        {
            FindEvent("EventEndRequest").DynamicInvoke(this, EventArgs.Empty);
        }
    
        private Delegate FindEvent(string name)
        {
            var key = typeof(HttpApplication)
                        .GetField(name, BindingFlags.Static | BindingFlags.NonPublic)
                        .GetValue(null);
    
            var events = typeof(HttpApplication)
                            .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
                            .GetValue(this) as EventHandlerList;
    
            return events[key];
        }
    }