0

I'm implementing an IHttpModule and am trying to write unit tests for it (using NUnit and Moq). I'm having a problem mocking the HttpApplication dependency for the Init method:

void Init(HttpApplication context);

Normally, ASP.NET controls the HttpApplication instance and passes it to the Init method. In the Init method, the custom IHttpModule subscribes to events published by the HttpApplication instance (like BeginRequest and EndRequest).

I need some way to mock the HttpApplication so I can raise the events and test that my implementation of the IHttpModule event handlers work.

I've tried creating a Mock HttpApplication in my test:

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

...but it gives the following error message:

Expression is not an event attach or detach, or the event is declared in a class but not marked virtual.

When I looked into that error, I learned that Moq can only mock Interfaces and virtual methods... So how can I mock a concrete class that I have no control over?

Here is the MyModule class:

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;
    }
 }   
Lews Therin
  • 3,707
  • 2
  • 27
  • 53
  • You can try with interface based work around. – cdev Mar 30 '19 at 02:04
  • @cdev Thanks for the response, but I'm having a hard time understanding what you mean. Can you explain it in further detail or with an example? – Lews Therin Mar 30 '19 at 02:05
  • Move methods used HttpApplication to class and interface and you can mock that class to expected behavior. Actually I prefer integration testing in scenarios like this. – cdev Mar 30 '19 at 02:10
  • @cdev I'm still not grasping what you mean. `HttpApplication` is a class by Microsoft in `System.Web`. I can't make it inherit a new interface or change `HttpApplication` at all. – Lews Therin Mar 30 '19 at 02:14
  • @LewsTherin when you have no control of 3rd party dependencies, the common practice is to encapsulate them behind abstractions you control, thus decoupling your code from things you have no control over. – Nkosi Mar 30 '19 at 02:34
  • Since this looks more like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), and you have not provided a [mcve] of the subject under test, it is difficult to see how to refactor the module to make it more testable. – Nkosi Mar 30 '19 at 02:37
  • Make your Init() method depend on an interface you define, not a concrete class. Then you can test the behavior of the Init() method. – chrisbuttacavoli Mar 30 '19 at 03:15
  • If what you are testing is the http call itself, then you shouldn’t be mocking that anyway...like cdev said, an integration test may be more appropriate here. – chrisbuttacavoli Mar 30 '19 at 03:16
  • @Nkosi I did provide a MCVE of the test, which is what the question is about. The class being tested only subscribes to the events in `HttpContext`, defines the `RequestTime` property, and sets that property in my `EndRequest` handler. What more do you need to know? – Lews Therin Mar 30 '19 at 04:09
  • @chrisbuttacavoli I can't change anything about `Init`, it already comes from the `IHttpModule` interface (which is controlled by Microsoft). I need to mock the concrete class (`HttpApplication`) because it is a dependency of the `Init` method which is required by implementing `IHttpModule`. I need to be able to mock it so I can raise the events that my `MyModule` subscribes to in my unit tests. – Lews Therin Mar 30 '19 at 04:13
  • @Nkosi I've added the class I'm testing. – Lews Therin Mar 30 '19 at 04:19
  • 1
    I wonder nobody suggested to write an integration tests. Unit tests have good feature - if you struggling to write tests. Architecture not designed for unit tests, if you not able to re-design and still want to automatically test it - use integration tests, which will cover whole pipeline of your application. – Fabio Mar 30 '19 at 07:11
  • @Fabio I will eventually add integration tests but I'm trying to focus on unit tests right now as that is part of the requirements for this task. – Lews Therin Mar 30 '19 at 17:00
  • Is requirement of the task to have automated tests? – Fabio Mar 30 '19 at 22:58
  • @Fabio Yes, unit tests. – Lews Therin Apr 03 '19 at 02:09
  • @LewsTherin, do you mean that business requires unit tests to be written, with your architecture you can easily write integration tests. – Fabio Apr 03 '19 at 03:36
  • @Fabio The requirements for this project specifically state that unit tests need to be written for this new `IHttpModule` implementation. – Lews Therin Apr 03 '19 at 14:18

1 Answers1

1

I don't think it's normally recommended, but you can use Reflection to mock non public/virtual members.

For current implementation, like this

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];
    }
}

Lews Therin
  • 3,707
  • 2
  • 27
  • 53
oika
  • 159
  • 4
  • This makes the unit test work, upvoted. Can you explain the code a little and expand on why you don't think it's normally recommended? – Lews Therin Mar 30 '19 at 17:12
  • Looking at the inner implementation of `HttpApplication` , event handlers of `BeginRequest` and `EndRequest` are held in `Events` property with key objects which defined as private field. Using reflection you can access to these members and fire event manually. Ofcause, these implementations are not public API and may possibly be changed someday, so I said it's not recommended. – oika Mar 31 '19 at 03:50