views:

1099

answers:

4
  1. I use Visual Studio 2008 Professional automated tests. I have a function that writes to a file. I want to unit test the file writing function. I have read somewhere that I would have to mock a file somehow. I don't know how to do it. Can you help?

  2. How to unit-test a method that downloads a page from the Internet?

+6  A: 

If the method has to open the file stream itself, then that's hard to mock. However, if you can pass a stream into the method, and make it write to that, then you can pass in a MemoryStream instead. An alternative overload can take fewer parameters, open the file and pass a FileStream to the other method.

This way you don't get complete coverage (unless you write a test or two which really does hit the disk) but most of your logic is in fully tested code, within the method taking a Stream parameter.

Jon Skeet
So I can split the method in 2, one opens the file stream and calls the second passing the file stream, that way I will be able to unit test the second one. A complete coverage test would read from the disk?
Jader Dias
Exactly. For test data which is most easily read in its own file, use Assembly.GetManifestResourceStream.
Jon Skeet
+2  A: 

It depends how close your code is to the nuts'n'bolts; for example, you could work in Streams instead, and pass a MemoryStream to the code (and check the contents). You could just write to the file-system (in the temp area), check the contents and ditch it afterwards. Of if your code is a bit above the file system, you could write a mockable IFileSystem interface with the high-level methods you need (like WriteAllBytes / WriteAllText). It would be a pain to mock the streaming APIs, though.

For downloading from the internet (or pretending to)... you could (for example) write an IWebClient interface with the functions you need (like DownloadString, etc); mock it to return fixed content, and use something like WebClient as the basis for an actual implementation. Of course, you'll need to test the actual implementation against real sites.

Marc Gravell
and testing the actual implementation against real site would be "integration tests" which most unit testing frameworks allow you to organise out from your standard tests. You'd run these integration tests less than your standard unit tests, as they would be slow (due to actually testing against "physical" things instead of just faked things.
Sekhat
+1  A: 

You don't actually want to make the call to write the file directly in your function but instead wrap the file I/O inside a class with an interface.

You can then use something like Rhino Mocks to create a mock class implementing the interface.

AndyM
The problem is that most non-trivial file-based work involves multiple (looped) calls to the streaming file APIs (readers/writers or streams) - very hard to mock without writing more code in the mock than the code you are testing.
Marc Gravell
+4  A: 

If the goal of your test is to test that the file is actually created, it is an integration test, and not a unit test.

If the goal it to test that the proper things are writen into the file, hide the file access behind an interface, and provide an in memory implementation.

The same is true for web page access.

interface IFileService
{
     Stream CreateFile(string filename); 
}

class InMemoryFileService : IFileService
{
    private Dictionary<string, MemoryStream> files = new Dictionary<string, MemoryStream>();

    public Stream CreateFile(string filename)
    {
       MemoryStream stream = new MemoryStream();
       files.Add(filename, stream);
       return stream;
    }

    public MemoryStream GetFile(string filename)
    {
         return files[filename];
    }
}

Using GetFile, you can find what should be written to disk.

Think Before Coding