0

I've got some code that's accessing the HttpContextBase.Trace object and doing some things with it (checking IsEnabled, writing some messages). The problem is that it's a System.Web.TraceContext object. TraceContext is a sealed class with only one constructor: that takes an HttpContext object. Moq is unable to mock either TraceContext or HttpContext. Can I do anything here to test this code with Moq?

Or will I need to factor this code out and stop referring to HttpContextBase.Trace?

RandomEngy
  • 14,931
  • 5
  • 70
  • 113
  • Have you looked at this question: http://stackoverflow.com/questions/1452418/how-do-i-mock-the-httpcontext-in-asp-net-mvc-using-moq – Anderson Imes Jul 26 '11 at 19:22
  • That does not talk about mocking the HttpContextBase.Trace property. It does include mocking several properties on it like Request and Response, but Trace (and the TraceContext object) is not included. – RandomEngy Jul 26 '11 at 19:53
  • 2
    Personally I would refactor to dependencies. Create an ITraceLogger and mock *that*. I won't put that as an answer because it doesn't really answer your question. Otherwise it looks like you might be limited to isolation frameworks that use the Profile API, like Typemock Isolator. In general, I'd really recommend looking at refactoring for testability. – Anderson Imes Jul 26 '11 at 20:41
  • "It's not possible" is an acceptable answer to me if it's accurate. – RandomEngy Jul 27 '11 at 03:04
  • 1
    It is possible if you wrap up the dependency like Anderson is suggesting. It is not possible if you are trying to use Moq to do it directly... (Or any other mocking library aside from Typemock Isolator or Microsoft Moles that can mock of static/sealed classes) – ElvisLives Jul 27 '11 at 15:59
  • @ElvisLives: I don't know why I always forget about Moles. Thanks for mentioning it. – Anderson Imes Jul 27 '11 at 16:05

1 Answers1

1

I sort of hate answers that say "don't do that" because there might be value in the answer, regardless of the approach, however, here we go:

Don't do that.

Let's assume you have this class:

public class MyCode
{
    public void Do()
    {
        HttpContext.Current.Trace.WriteLine("WOO!");
    }
}

This kind of thing isn't very testable. If you wanted to refactor for testability, you could use more of an "inversion of control" type of method. Here I'll use "dependency injection" (there are other options, like "service location" and "abstract factories", but this is easiest to understand):

public class MyCode
{
    private IMyLogger _logger = null;
    public MyCode(IMyLogger logger)
    {
        _logger = logger;
    }

    public void Do()
    {
        _logger.TraceWriteLine("WOO!");
    }
}

Now you can see that this code is very testable and you don't have to jump through hoops to mock anything.

//Confirms "Do" method calls "TraceWriteLine"
public void Do_Called_CallsTraceWriteLine()
{
    //Arrange
    var loggerMock = new Mock<IMyLogger>();
    loggerMock.Setup(l => l.TraceWriteLine(It.IsAny<string.());

    var target = new MyCode(loggerMock.Object);

    //Act
    target.Do();

    //Assert
    loggerMock.VerifyAll(); 
}

Now your implementation of IMyLogger might call out to HttpContext, but it keeps your target class testable.

public DefaultLogger : IMyLogger
{
    public void TraceWriteLine(string message)
    {
        HttpContext.Current.Trace.WriteLine(message);
    }
}

To implement such a thing, many people choose to use an Inversion of Control container. You don't have to do this, but it makes things a little simpler and doesn't decrease maintainability as you add more dependencies. You can imagine if you had an interface for each part of the .NET framework that was untestable, your "new MyCode" constructor calls would start to get quite long. IoC containers help you avoid this.

Popular IoC containers for .NET:

  • Ninject
  • Unity
  • MEF (builtin to .NET 4.0)
  • Autofac
  • Castle Windsor
  • StructureMap

Your post is tagged "ASP.NET". Hopefully you are using MVC. If so, it has new support for dependency injection, examples here: http://weblogs.asp.net/shijuvarghese/archive/2011/01/21/dependency-injection-in-asp-net-mvc-3-using-dependencyresolver-and-controlleractivator.aspx

Hopefully this helps. Sorry it doesn't directly answer your question.

Anderson Imes
  • 25,500
  • 4
  • 67
  • 82
  • I've been going through a legacy ASP.NET system (not MVC) and refactoring to add unit tests. Been extracting out dependencies and using Unity to do so. I do realize that extracting out all the calls to an ITracer would work, but I was a bit thrown by the fact that basically every property on the HttpContextBase class is mockable except for the Trace property and the TraceContext class. It seems like an odd omission so I thought I would ask about it. – RandomEngy Jul 27 '11 at 20:41
  • @RandomEngy: Those properties just *happen* to be mockable. Very large parts of the framework are pretty untestable. If you ever do any IO, you'll see what I mean. Someone above mentioned Microsoft Moles as an alternative mocking framework. It doesn't quite use the profile API, but it has the same capabilities as mocking frameworks that do and it's free. – Anderson Imes Jul 27 '11 at 20:51
  • I thought the whole reason they came up with HttpContextBase, HttpRequestBase, HttpResponseBase, HttpBrowserCapabilitiesBase etc was to make the HttpContext object mockable. HttpContext happens _not_ to be mockable, but HttpContextBase (and just about all of its properties) were _designed_ to be that way. I was curious about the Trace property because **in the context of the HttpContextBase object** it stood out as non-mockable. – RandomEngy Jul 27 '11 at 21:50
  • @RandomEngy: I don't think that was the entire goal of the thing. If you look at comments from Microsoft about this, there's really no illusions that any part of context data is meant to be truly mockable. Here's a good comment from Microsoft on a Connect question: http://connect.microsoft.com/VisualStudio/feedback/details/376426/asp-net-abstractions-add-httpcontextbase-current – Anderson Imes Jul 27 '11 at 22:07
  • That link is not talking about mocking a certain _part_ of the HttpContext, it's talking about _accessing the object itself_. They're arguing against allowing access to it via HttpContextBase.Current. This is a separate issue; it just looks like they neglected to wrap the TraceContext type to allow mocking. – RandomEngy Jul 27 '11 at 23:21
  • That's what I was pointing out, though.. they aren't really focusing on HttpContextBase as a part of the framework that is meant for mocking. I realize the comment and question weren't geared toward the Trace property, but it brings to light what they had in mind for that class and it wasn't mocking. The other properties were mockable, but it seems like that's just a coincidence. – Anderson Imes Jul 28 '11 at 03:14
  • It seems like a coincidence to you that every other property backed by a sealed class on HttpContext was wrapped to allow mocking. HttpSessionStateBase, HttpRequestBase, HttpResponseBase, HttpServerUtilityBase, ProfileBase, HttpApplicationStateBase? I guess they were just wrapping those because it's just fun to wrap types and they picked those at random. – RandomEngy Jul 28 '11 at 16:13
  • @RandomEngy I think those were wrapped for other purposes, yes. You can tell by the comments from Microsoft that they don't intend HttpContext to be a valid concern for mocking. It has static context and things with static context aren't mockable. I'm not saying the whole thing is ideal, I'm just saying that you should be aware that mocking HttpContext is not something that Microsoft expects you to do and it's not really a supported scenario. The fact that certain parts of the API support a form of it is incidental. – Anderson Imes Jul 28 '11 at 16:32
  • @AndersonImes let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1929/discussion-between-randomengy-and-anderson-imes) – RandomEngy Jul 28 '11 at 16:43