C# Unit Testing: Testing a method that uses MapPath
Asked Answered
B

4

19

First of all, I am aware that this question is dangerously close to: How to MapPath in a unit test in C#

I'm hoping however, that it has a different solution. My issue follows:

In my code I have an object that needs to be validated. I am creating unit tests for each validation method to make sure it is validating correctly. I am creating mock data and loading it into the object, then validating it. The problem is that within the validation, when an error occurs, an error code is assigned. This error code is used to gather information about the error from an xml file using Server.MapPath. However, when trying to get the xml file, an exception is thrown meaning the file cannot be found.

Since MapPath is in my validation code, and not my unit test, how do I get my unit test to recognize the path? Does this question make sense?

Error Line (In my Validation code NOT my unit test):

XDocument xdoc = XDocument.Load(HttpContext.Current.Server.MapPath("App_Data/ErrorCodes.xml"));

Simplified: The Unit Test calls a method in my program that calls Server.MapPath which then fails.

Biannulate answered 11/8, 2009 at 17:7 Comment(0)
M
15

I would abstract out the "filename provider" into an class that simply returns a location, then you can mock it much, much easier.

public class PathProvider
{
    public virtual string GetPath()
    {
        return HttpContext.Current.Server.MapPath("App_Data/ErrorCodes.xml");
    }
}

Then, you can either use the PathProvider class directly...

PathProvider pathProvider = new PathProvider();
XDocument xdoc = XDocument.Load(pathProvider.GetPath());

Or mock it out in your tests:

PathProvider pathProvider = new MockPathProvider(); // using a mocking framework
XDocument xdoc = XDocument.Load(pathProvider.GetPath());
Mureil answered 11/8, 2009 at 17:30 Comment(5)
GetPath() should probably be virtual.Battement
@Dan HttpContext.Current. returns null when we run from TestProject. Any way to avoid this?Hepatic
@JibinMathew Yes, don't use HttpContext.Current. I know that sounds facetious but it's true. Generally, don't mock classes you don't own, but you can write a wrapper class for HttpContextBase which implements your desired functionality.Harrus
@DanAtkinson HOWHydrolytic
@Hydrolytic I can't tell if this is a question or not. If it it, check whether it's already been answered and, if not, ask it as a question and not a comment.Harrus
B
11

After some rigorous googling and some help from a colleague we came up with a simple solution already built into .net

Above the unit tests that accesses the validation process, I added:

 [TestMethod()]
 [HostType("ASP.NET")]
 [UrlToTest("http://localhost:###/upload_file.aspx")]
 [AspNetDevelopmentServerHost("Path To Web Application", "Path To Web Root")]

This works perfectly. Basically, when the test is called, it loads the URL with the specified unit test in the page load. Since it is a web site that is now calling the unit test, the validation will have access to Server.MapPath. This solution may not work for everyone, but it was perfect for this. Thanks to all you contributed.

Biannulate answered 11/8, 2009 at 20:33 Comment(3)
Integration tests like this are certainly useful to make sure all the pieces work together, but there are other scenarios (like a missing file) that this solution wouldn't be able to handle. Spawning the test web application is also a rather expensive operation, which can add up if you run tests frequently.Battement
agree, while this solution has some merit in the sense that it can provide useful info when run, it's not a unit test in any sense of the word and it lacks moste benefits of actual unit tests.Astigmia
that's a great time saver!!Margravine
C
1

Try using Rhino Mocks or an alternative mocking framework to Mock the httpContext (or other dependent objects) Or you could write your own mock objects. Or write a MapPathWrapper class, inherit from a MapPathWrapperBase class for your real environment, then in for your unit tests create a MockMapPathWrapper object.

There should be plenty of examples for mocking on SO.

Here's one I asked:

How to use Rhino Mocks to Mock an HttpContext.Application

UPDATE I only have experience doing this with the Asp.Net MVC, with webforms I imagein it would be a lot more difficult because of the lack of an HttpContextBase class.

Comely answered 11/8, 2009 at 17:22 Comment(0)
B
1

I would extract methods that accept your dependencies as arguments:

public void Validate(HttpContext context)
{
    ValidatePath(context.Server.MapPath("App_Data/ErrorCodes.xml"));
}

public void ValidatePath(string path)
{
    XDocument xdoc = XDocument.Load(path);
    ValidateDocument(xdoc);
}

public void ValidateDocument(XDocument xdoc)
{
    // Original code
}

You can then test the various methods independently. For example, testing how ValidatePath() handles a missing file.

Battement answered 11/8, 2009 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.