14

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.

David Glenn
  • 23,572
  • 17
  • 70
  • 94

6 Answers6

24

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).

kastermester
  • 3,008
  • 6
  • 25
  • 43
  • What's the benefit of Interface here?. Instead, can we simply make 2 class files and call them accordingly 1 from an actual app and 1 from test endpoint ? Can someone please explain the benefit of this interface here? – Sebastian May 30 '16 at 11:26
  • By using classes you need to decide to have one class inherit from the other to make the type system happy. Having either your test implementation inherit from the real one, or the other way around is not really an ideal solution. Doing it this way you need not make your real and test implementations know about each other at all - they just have a common interface they need to satisfy. – kastermester Jun 08 '16 at 08:45
5

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

Community
  • 1
  • 1
zebrabox
  • 5,556
  • 25
  • 32
  • Just to be clear, I'm not testing whether the XML can be loaded or testing the contents of the file, I'm testing a piece of code that requires an XDocument to be parsed. The file is located with the tests if it doesn't load then the test will show an error and not a false negative. I realize this isn't ideal but I don't know of any other way. I've updated my question. – David Glenn Aug 05 '09 at 09:09
  • For sure that's the point though isn't it? Your code is testing your parser so where it gets the xml data from is irrelevant and it shouldn't fail because the file doesn't exist. Personally, I'd go with the string option or you could have it as an embedded resource – zebrabox Aug 05 '09 at 09:16
3

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();
  }
}
Dan Atkinson
  • 10,801
  • 12
  • 78
  • 106
Grzenio
  • 33,623
  • 43
  • 148
  • 226
2

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.

Juri
  • 30,146
  • 17
  • 97
  • 132
1

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");
}
BrunoLM
  • 88,362
  • 76
  • 272
  • 427
0

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();
...
}