24

The goal is to run some tests given some data in those Xml files.

How would you easily load a given Xml file into an XmlDoc within the unit test methods?

Current state is:

  XmlDocument doc = new XmlDocument();
  string xmlFile = "4.xml";
  string dir = System.IO.Directory.GetCurrentDirectory() + @"\Msgs\" 

  //dir is then the value of the current exe's path, which is
  //d:\sourcecode\myproject\TestResults\myComputer 2009-10-08 16_07_45\Out

  //we actually need:
  //d:\sourcecode\myproject\Msgs\ 
  doc.Load( dir + fileName); //should really use System.IO.Path.Combine()!

Is it just a simple matter of putting that path in an app.config? I was hoping to avoid that, given the possibility of different paths on developer machines.

Question: How would you write the algorithm to load a given Xml file into an XmlDocument in the unit test method?

Erik
  • 820
  • 12
  • 22
p.campbell
  • 98,673
  • 67
  • 256
  • 322

7 Answers7

27

There is a Visual Studio Unit Testing feature for this: DeploymentItemAttribute

I use this feature to copy all xml files in a given project folder to the unit test output folder, before testing if all required files are present.

You can use this attribute with your unit tests to copy specific files from the Project folder (or anywhere else) to the Unit Test output folder. Like so:

[TestMethod()]
[DeploymentItem("MyProjectFolder\\SomeDataFolder\\somefile.txt", "SomeOutputSubdirectory")]
public void FindResourcefile_Test()
{
    string fileName = "SomeOutputSubdirectory\\somefile.txt";
    Assert.IsTrue(System.IO.File.Exists(fileName));
}

You can also copy the contents of whole folders:

[TestMethod()]
[DeploymentItem("MyProjectFolder\\SomeDataFolder\\", "SomeOutputSubdirectory")]
public void FindResourcefile_Test()
{
    string fileName = "SomeOutputSubdirectory\\someOtherFile.txt";
    Assert.IsTrue(System.IO.File.Exists(fileName));
}

The first parameter is the source, the second the destination folder. The source is relative to your solution folder (so you can access the Unit Test project of the project being tested) and the destination is relative to the output folder of the unit test assembly.

UPDATE:

You need to enable Deployment in the Test Settings for this to work. This MSDN page explains how (it's real easy): http://msdn.microsoft.com/en-us/library/ms182475(v=vs.90).aspx#EnableDisableDeploy

Pieter Müller
  • 4,573
  • 6
  • 38
  • 54
  • This does not work with **NUnit**. `DeploymentItemAttribute` is in `Microsoft.VisualStudio.TestTools.UnitTesting` – Jess Mar 08 '16 at 20:48
  • Note that you must set the property "copy to output" of the file you want to use : Select them in Solution Explorer and set the Copy to Output property to Copy if Newer. See this link : https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.deploymentitemattribute(v=vs.140).aspx – Allie May 12 '16 at 11:26
24

You can build those files into your executable (set their "Build Action" property to "Embedded Resource") and then get them using the Assembly.GetManifestResourceStream method.

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • Patrick Cauldwell has an interesting article about that [http://www.cauldwell.net/patrick/blog/TestingWithExternalFiles.aspx](http://www.cauldwell.net/patrick/blog/TestingWithExternalFiles.aspx) – AlexanderD Jul 13 '16 at 10:09
11

In the unit test project add a post-build event that copies the XML file to the output directory. Then, you can use your original code to get the XML file.

The post build event will look like something like this:

copy $(SolutionDir)file.xml $(ProjectDir)$(OutDir)file.xml

You may also need this to add to your path:

Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
bzlm
  • 9,626
  • 6
  • 65
  • 92
HitLikeAHammer
  • 2,679
  • 3
  • 37
  • 53
  • 14
    I'm finding that my file is going to "Project1_UnitTests\bin\Debug" whereas the test is trying to find it under "TestResults\Username_PCName_datetime\Out"..??? – Greg Oct 14 '09 at 07:08
  • per the 6 upvotes on @Greg's comment, I've edited this answer and added in the additional information to make this answer more complete. – javamonkey79 Mar 02 '12 at 03:53
  • 1
    @javamonkey79 That doesn't help java – Chuck Savage May 15 '12 at 05:33
  • You don't need a post-build step to do this. Simply set the "Copy to output directory" property on the data files. – Dominic Cronin Jul 12 '12 at 19:47
  • 6
    @DominicCronin that is not true. If the data files are part of the project under test, they will be copied to the debug folder of the project, but they will not be copied to the unit test output folder, which is the location of the executing assembly during unit tests. – Pieter Müller Jul 25 '12 at 11:00
  • @PieterMüller I had it working with settings that made it look like that was happening, but just recently it suddenly stopped working, and I have no idea what change caused this. I haven't investigated it fully - my timetable is such that I simply moved everything to embedded resources, and gave up on the other approach. Still - I suspect this means you are right. – Dominic Cronin Jul 29 '12 at 16:47
5

I use a helper class to deal with getting basic paths I might want to access in my Unit Tests.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Brass9.Testing
{
    public static class TestHelper
    {
        public static string GetBinPath()
        {
            return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        }

        public static string GetProjectPath()
        {
            string appRoot = GetBinPath();
            var dir = new DirectoryInfo(appRoot).Parent.Parent.Parent;
            var name = dir.Name;
            return dir.FullName + @"\" + name + @"\";
        }

        public static string GetTestProjectPath()
        {
            string appRoot = GetBinPath();
            var dir = new DirectoryInfo(appRoot).Parent.Parent;
            return dir.FullName + @"\";
        }

        public static string GetMainProjectPath()
        {
            string testProjectPath = GetTestProjectPath();
            // Just hope it ends in the standard .Tests, lop it off, done.
            string path = testProjectPath.Substring(0, testProjectPath.Length - 7) + @"\";
            return path;
        }
    }
}

Sometimes my interactions with paths are more complex; I often use a central class I name "App" to indicate some basic details about the application, like its root folder, its root namespace and module, etc. Classes will sometimes depend on App's existence, and so instead I'll place an init method on App that uses code like the above to initialize itself for test harnesses, and call that method from the Init command in a Unit Test.

(Updated)

Old Answer

I found this helps for getting arbitrary paths to access files in the project folder you intend to test (as opposed to files in the Test project folder, which can make busywork if you need to copy things over).

DirectoryInfo projectDir = new DirectoryInfo(@"..\..\..\ProjectName");
string projectDirPath = projectDir.FullName;

You can then use either of those variables to access whatever you need from the related project. Obviously swap "ProjectName" out for the actual name of your project.

Community
  • 1
  • 1
Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
  • Or, if your path looks like "TestResults\Username_PCName_datetime\Out", you can use `ProjectDir = new System.IO.DirectoryInfo(@"..\..\..\..\..\..\ProjectName");` – Jan 'splite' K. Oct 12 '11 at 14:08
  • I have ran into problems using this method. One path will work for MS Test runner but for other runners it might not work. For example some developers were using **MS Test Runner** and some **ReSharper** and the test paths were different (\TestResults\\) and failed one or the other way. Fixed by a hack :/ – J Pollack Jun 01 '12 at 13:55
1

Resources are just resources and that's it, no need to complicate. If you don't want to embed them then you could add these files as "Content" resources to your project and set them to Copy always. Then specify the sub-folder in your code:

var xmlDoc = XElement.Load("ProjectSubFolder\\Resource.xml");

This will automatically load the resources from the project output (running assembly location) bin\$(Configuration)\ResourceSubfolder\

This works for all types of projects, not just unit tests.

vezenkov
  • 4,009
  • 1
  • 26
  • 27
0

I would just put the path in the app.config and load from the default path. In my team, i am really anal about developers changing paths, so i make all my developers have the same exact paths and files on their computers, so i dont have an issue of any rogue developer changing a path to suite his workspace.

For example , all developers in my team must use C:\Project\Product\Module, etc etc. I also make sure all their software installed also is standard. This way, i can ghost any machine into any other easily.

Andrew Keith
  • 7,515
  • 1
  • 25
  • 41
0

I think in VS.NET 2012 DeploymentItem attribute works without any Test Settings configuration.

freeze
  • 9
  • 3