47

I'm using Visual Studio 2008 with Microsoft test tools. I need to access a text file from within the unit test.

I've already configured the file with build action set to 'Content' and copy to output directory to 'Copy Always', but the file is not being copied to the output directory, which according to System.Environment.CurrentDirectory is

'{project_path}\TestResults\Pablo_COMPU 2009-11-26 15_01_23\Out'

This folder contains all the DLL dependencies of the project, but my text file is not there.

Which is the correct way to access a text file from a unit test?

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
Pablote
  • 4,745
  • 9
  • 39
  • 46

5 Answers5

56

You have to add the DeploymentItem attribute to your test class. With this attribute you specify the files which are copied into the out directory for the test run.

For example:

[TestMethod]
[DeploymentItem(@"myfile.txt", "optionalOutFolder")]
public void MyTest()
{
    ...
}

See also: http://msdn.microsoft.com/en-us/library/ms182475.aspx.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
Alexander Egger
  • 5,132
  • 1
  • 28
  • 42
20

Alternatively if you set all your text files to "Copy to build directory" then you could reference their path in your tests by doing this

var directory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var path = System.IO.Path.Combine(directory, "myFile.txt");
Jimmie Clark
  • 1,878
  • 13
  • 25
Gavin
  • 17,053
  • 19
  • 64
  • 110
  • .. except GetExecutingAssembly returns null when called from unmanaged code, e.g. the Resharper 2016.2 test runner. – stuartd Sep 21 '16 at 11:30
  • 2
    It's best to use `System.IO.Path.Combine()` instead of that string concatenation. Otherwise, this works for me in a Unit Test scenario. – Micah Epps May 06 '19 at 14:11
  • I dont think this works on a [TestMethod], it will throw an exception since you're using a binary test harness ( it can return null when called from unmanaged code) so you will get an exception. – Rolando Retana Sep 07 '19 at 11:30
  • I agree with Micah, and posted an edit to the source. – Jimmie Clark Jan 21 '21 at 15:56
4

When I need a chunk of text as part of a unit test and it's more than a line or two, I use an embedded resource. It doesn't clutter your test code, because it's a separate text file in the source code. It gets compiled right into the assembly, so you don't have to worry about copying around a separate file after compilation. Your object under test can accept a TextReader, and you pass in the StreamReader that you get from loading the embedded resource.

Don Kirkby
  • 53,582
  • 27
  • 205
  • 286
3

I can't answer your question as I don't use MSTest. However, I'd consider whether accessing the file system in a unit test is the right thing to do. If you introduce a dependency on the file system, the test will become slower and less trustworthy (you now depend on something that may not be there/accessible/etc). It is for these reasons that many folk will say "it's not a unit test if it hits the file system".

Although this is not a hard rule, it's always worth considering. Any time I have to touch the file system in tests, I try to avoid it because I find tests that rely on files are harder to maintain and are generally less consistent.

I'd consider abstracting the file operations to some degree. You can do numerous things here, from changing the internal loading strategy (via Dependency Injection) to -- even better -- separating the loading/use of the file so that the consumer of the file's contents doesn't even have to care about the loading strategy.

Community
  • 1
  • 1
Mark Simpson
  • 23,245
  • 2
  • 44
  • 44
  • 1
    +1 Streams or TextReader/TextWriter are usually quite sufficient abstractions. – Mark Seemann Nov 27 '09 at 08:00
  • 2
    Not sure where you are getting the idea that testing with files is bad. It's not. Reading a quick string from a text file is NOT a slow operation, and having a test file part of your test (same directory) does not make is less trustworthy. – vicsz Nov 27 '09 at 16:04
  • 5
    I 'got the idea' from working on a project with 10k tests maintained by various people. The ones that touch files are more fickle, harder to maintain and run slower than tests that do not hit the file system. Example: we had to get someone around during a refactoring session to fix up their tests, as we were getting file not found errors despite the file being present and copied to the output directory. It turned out to be a fairly obscure bug in the test that was order dependent. That simply wouldn't happen with an in memory unit test. – Mark Simpson Nov 27 '09 at 16:50
  • 3
    Btw, I'm not saying "never" do it, I'm just saying that if it's possible to avoid it without causing too much trouble, avoid it! – Mark Simpson Nov 27 '09 at 16:51
  • 2
    My scenario involves testing a deserializer which uses XML. I want to test the deserializer, not the XML. So in my case, XML as a file is perfectly legit. – Micah Epps May 06 '19 at 14:15
2

How are you running your tests?

We use (TestDriven.net -> Run Tests).

From my experience, some test runners (like Junit in Netbeans) won't automatically copy any additional text files that you might need for testing. So in your case you might have to do a full build, and then try running the tests again.

And the correct way for accessing text files from tests is the way you're trying to do it. (Setting the files to "copy always" and "content", and accessing them from the compiled directory).

Also, not sure where people are getting the idea that having tests rely on files is a bad thing. It's not.

If anything, having separate test files, will only clean up your tests and make them more readable. Consider some xml parsing method that returns a large string:

String expectedOutput = fileOperator.ReadStringFromFile("expectedFileContents.xml");
String result = systemUnderTest.Parse(somethingtoparse);
Assert.Equals(expectedOutput, result);

Imagine if the output was 30 lines long, would you clutter your test code with one giant String? or just read it from a file.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
vicsz
  • 9,552
  • 16
  • 69
  • 101