David Glenn
David Glenn

Reputation: 24532

How to MapPath in a unit test in C#

I want to load an external XML file in a unit test to test some processing code on that XML. How do I get the path of the file?

Usually in a web app I would do:

XDocument.Load(Server.MapPath("/myFile.xml"));

But obviously in my unit test I have no reference to Server or HttpContext so how can I map a path so that I don't have to specify the full path?

UPDATE:

I just want to make it clear that the code I'm actually testing is for an XML parser class, something like:

public static class CustomerXmlParser {
  public static Customer ParseXml(XDocument xdoc) {
    //...
  }
}

So to test this I need to parse a valid XDocument. The method being tested does not access the file system itself. I could create the XDocument from a String directly in the test code but I thought it would be easier to just load it from a file.

Upvotes: 14

Views: 11534

Answers (6)

Natasha Voloshyna
Natasha Voloshyna

Reputation: 797

This may be helpful to someone. I had a related issue. Wanted to use an Excel file from a root-level folder within my c# Unit Test project.

I had a root-leve folder named "TestFiles". Inside I had "Test.xlsx".

What i did was:

Right-click on the "Test.xlsx", go to Properties and set "Copy To Output Directory" = "Copy Always"

Now the file and its containing folder "TestFiles" always get copied into the bin folder of the Unit Test project. So that I was able to use it like so:

var filePath = "TestFiles/Test.xlsx";
var strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=0\"";
using (var conn = new OleDbConnection(strConn))
{
                conn.Open();
...
}

Upvotes: 0

BrunoLM
BrunoLM

Reputation: 100381

Classes:

internal class FakeHttpContext : HttpContextBase
{
    public override HttpRequestBase Request { get { return new FakeHttpRequest(); } }
}

internal class FakeHttpRequest : HttpRequestBase
{
    public override string MapPath(string virtualPath)
    {
        return /* your mock */
    }
}

Usage:

[TestMethod]
public void TestMethod()
{
    var context = new FakeHttpContext();
    string pathToFile = context.Request.MapPath("~/static/all.js");
}

Upvotes: 1

Grzenio
Grzenio

Reputation: 36679

Usually for unit tests I add the xml files as embedded resources to the project and load them using a method like this:

public static string LoadResource(string name)
{
  Type thisType = MethodBase.GetCurrentMethod().DeclaringType;
  string fullName = thisType.Namespace + "." + name + ".xml";

  using (Stream stream = thisType.Module.Assembly.GetManifestResourceStream(fullName))
  {
      if(stream==null)
      {
        throw new ArgumentException("Resource "+name+" not found.");
      }

      StreamReader sr = new StreamReader(stream);
      return sr.ReadToEnd();
  }
}

Upvotes: 3

Juri
Juri

Reputation: 32930

Edit: I'm starting from scratch since I guess I interpreted your question the wrong way initially.

The best way to load an XML file in your unit test for injecting it then to some of your classes is to use the DeploymentItem attribute in MS unit tests.

This will look like the following:

[TestMethod]
[DeploymentItem(@"DataXmlFiles\MyTestFile.xml", "DataFiles")]
public void LoadXMLFileTest()
{
   //instead of "object" use your returning type (i.e. string, XDocument or whatever)
   //LoadXmlFile could be a method in the unit test that actually loads an XML file from the File system
   object myLoadedFile = LoadXmlFile(Path.Combine(TestContext.TestDeploymentDir, "DataFiles\\MyTestFile.xml"));

   //do some unit test assertions to verify the outcome
}

I didn't test the code now on a debugger, but it should work.

Edit: Btw, when you use DeploymentItem consider this post here.

Upvotes: 2

kastermester
kastermester

Reputation: 3064

Another idea would be to utilize dependency injection.

public interface IPathMapper {
string MapPath(string relativePath);
}

And then simply use 2 implementations

public class ServerPathMapper : IPathMapper {
     public string MapPath(string relativePath){
          return HttpContext.Current.Server.MapPath(relativePath);
     }
}

And then you also need your mock implementation

public class DummyPathMapper : IPathMapper {
    public string MapPath(string relativePath){
        return "C:/Basedir/" + relativePath;
    }
}

And then all your functions that needs to map path's would simply need to have access to an instance of IPathMapper - in your web app it needs to be the ServerPathMapper and in your unit tests the DummyPathMapper - basic DI (Dependency Injection).

Upvotes: 24

zebrabox
zebrabox

Reputation: 5774

Personally, I'd be very wary about having any code that relies on a back-end resource store, be that a file system or a database - you are introducing a dependency into your unit test that is likely to lead to false negatives i.e tests failing not because of your specific test code but because the file isn't there or the server is unavailable etc.
See this link for IMO a good definition of what a unit test is and more importantly is not

Your unit test should be testing an atomic, well-defined piece of functionality not testing whether a file can load. One solution is to 'mock' the file load - there are various approaches to this however, I'd personally only mock the interface to the file system your are using and not try and do any full filesystem mocking - here's a good SO post and here's a good SO discussion on file system mocking

Hope that helps

Upvotes: 5

Related Questions