单元测试垃圾收集

时间:2014-03-25 14:43:29

标签: c# unit-testing moq mstest

问题
在测试完成后,单元测试是否通过垃圾收集(System.IO.Stream)自动处理资源,或者是否处于打开/正在使用状态,需要处理IDisposable个对象? / p>

背景/信息
我目前正在对使用System.IO.Stream的文件上传器进行单元测试 我已经使用由HttpPostedFileBase驱动的文件的InputStream将System.IO.MemoryStream带出[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(); } } ,这一切都按预期工作。

我目前(为了简洁起见):

MemoryStream

问题在于创建的var stream = new FunctionCreatingTheMemoryStream(); 实例:

try catch

是否值得在finally之后放置任何代码,然后在[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 } 语句中处理流,或者将其作为单元测试,是否会自动处理内存流?

所以有必要这样做,或者它可以只是:

{{1}}

3 个答案:

答案 0 :(得分:7)

答案最终取决于您使用的单元测试框架,但在.NET中,三个主要测试框架(MSTest,NUnit,xUnit.net)都没有自动处理。你必须手动要求他们这样做。

您可能会争辩说,在执行测试套件时,测试运行器原则上会启动一个新进程,运行所有测试,然后退出该进程。

对于IDisposable的某些实现,如MemoryStream,这只是意味着在进程退出时回收内存。但是,您不能总是依赖它,因为某些一次性类型可能会访问进程外资源。理论上,您可以将对象保存到内存映射文件,命名管道,SQL Server连接等。即使测试进程退出,您也可能留下这些资源。它们可能迟早会超时(比如SQL Server连接返回池中),但它可能会降低系统速度。

此外,一些测试运行者现在非常努力变得聪明,因此他们重用一个或多个进程以便能够更快地运行,从而将测试套件更改为进出AppDomains。

所以,最后,除非你有MemoryStream这样的东西,你绝对肯定将它留下来并不是什么大不了的事,你应该在测试中确定地处理你的对象。 / p>

但是,如果您正在进行测试驱动开发,则应采用倾听测试的GOOS态度。如果您编写很多涉及IDisposable对象的测试,您应该考虑是否可以简化SUT的API。这取决于你正在做什么,但如果你写的主要是托管代码,你不应该IDisposable多,因为它是一个 Leaky Abstraction 泄漏SUT依赖于非托管资源..

答案 1 :(得分:1)

在过去,我编写了一个实现IDisposable的“Dustcart”类,并包含要处理的对象集合。它们按操作顺序排列,以便它们如何添加(使用堆栈来实现它)。

然后测试代码看起来像。

[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()是一种通用方法。你可以把它放在一个超级类中,你所有的测试都是固有的,对于你需要模拟的类等也包括o bject mothers

但是,如果您不是非常小心,您的测试代码将变得难以理解,而您正在测试的软件质量没有任何实际好处。这些测试是为了完成一项工作而且他们必须要比完成这项工作所需要的更好。

答案 2 :(得分:0)

完全同意每个人你需要处理这些物品。您可以使用“using”语句简化代码,如下所示:

[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
  }
}