Question

Question
Do unit tests automatically dispose resources via garbage collection (System.IO.Stream in my case) once the test(s) have completed, or are things left open/in-use, requiring the disposing of IDisposable objects?

Context / Information
I'm currently making a unit test for a file uploader which uses a System.IO.Stream.
I've Moq'd out the HttpPostedFileBase with the InputStream of the file powered by a System.IO.MemoryStream, which all works as expected.

I currently have (altered for brevity):

[TestMethod]
public void TestUpload()
{
  var stream = FunctionCreatingTheMemoryStream();
  try
  {
    var file = new Mock<HttpPostedFileBase>();
    file.Setup(f => f.FileName).Returns("test.txt");
    file.Setup(f => f.InputStream).Returns(stream);  
    MethodThatUsesTheStream(file.Object)  
    // rest of test code with Assert
  }
  finally
  {
    stream.Dispose();
  }
}

The question is with the MemoryStream instance that is created:

var stream = new FunctionCreatingTheMemoryStream();

Is it worthwhile placing any code after this in a try catch and then disposing of the stream in the finally statement, or with it being a unit test, will the memory stream be disposed of automatically?

So is it necessary to do this, or could it simply just be:

[TestMethod]
public void TestUpload()
{
   var stream = FunctionCreatingTheMemoryStream();
   var file = new Mock<HttpPostedFileBase>();  

   file.Setup(f => f.FileName).Returns("test.txt");
   file.Setup(f => f.InputStream).Returns(stream);  
   MethodThatUsesTheStream(file.Object)
   // rest of test code with Assert
}
Était-ce utile?

La solution

The answer ultimately depends on the unit testing framework you use, but in .NET, none of the three major test frameworks (MSTest, NUnit, xUnit.net) automatically dispose of things. You have to manually ask them to do so.

You could argue that when executing a test suite, the test runner in principle launches a new process, runs all the tests, and then the process exits.

For some implementations of IDisposable, like MemoryStream, that simply means that the memory is reclaimed when the process exits. However, you can't always rely on that, because some disposable types may access out-of-process resources. You could theoretically have objects holding on to memory-mapped-files, named pipes, SQL Server connections, etc. Even if the test process exits, you may leave behind such resources. They'll probably time out sooner or later (like SQL Server connections returning to the pool), but it may slow down your system.

Furthermore, some test runners are attempting very hard to be clever these days, so they reuse one or more processes to be able to run faster, changing your test suite in and out of AppDomains.

So, in the end, unless you have something like a MemoryStream, where you're absolutely certain that it's not a big deal to leave it behind, you should deterministically dispose of your objects in your tests.

However, if you're doing Test-Driven Development, you should adopt the GOOS attitude of listening to your tests. If you write a lot of tests that involve IDisposable objects, you should consider if you can simplify your SUT's API. It depends on what you're doing, but if you write mostly managed code, you shouldn't need IDisposable much, because it's a Leaky Abstraction that leaks that the SUT depends on unmanaged resources..

Autres conseils

In the past I have written a "Dustcart" class that implements IDisposable, and contains a collection of objects to dispose. These are disposed in the opersite order to how they are added (use a stack to implement it).

Then the test code looks like.

[Setup]
Public void Setup()
{
    _dustcart = new Dustcart()
}

[TearDown] 
public void TearDown ()
{
  _dustcart.Dispose();
}

[TestMethod]
public void TestUpload()
{
   var stream = _dustcart.DisposeOnTearDown(FunctionCreatingTheMemoryStream());
   var file = new Mock<HttpPostedFileBase>();  

   file.Setup(f => f.FileName).Returns("test.txt");
   file.Setup(f => f.InputStream).Returns(stream);  
   MethodThatUsesTheStream(file.Object)
   // rest of test code with Assert
}

DisposeOnTearDown() is a generic method. You could put this this in a super class that all your tests inherent from, that also includes object mothers for the classes you need to mock etc.

However if you are not very careful your test code becomes harder to understand with no real benefit to the quality of the software you are testing. The tests are there to do a job and they have to be no better than is needed to do that job.

Totally agree with everyone that you need to dispose such objects. You can simplify your code a bit by using "using" statements, like this:

[TestMethod]
public void TestUpload()
{
  using (var stream = FunctionCreatingTheMemoryStream())
  {
    var file = new Mock<HttpPostedFileBase>();
    file.Setup(f => f.FileName).Returns("test.txt");
    file.Setup(f => f.InputStream).Returns(stream);  
    MethodThatUsesTheStream(file.Object)  
    // rest of test code with Assert
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top