如何使用反射对 Dispose() 进行单元测试?

How to unit test Dispose() using reflection?

我想为实现 IDisposable 的 class 编写单元测试。 class 有许多私有字段也实现了 IDisposable。在我的测试中,我想验证当我调用 Dispose() 时,它会在其所有 IDisposable 字段上正确调用 Dispose()。本质上,我希望我的单元测试看起来像这样:

var o = new ObjectUnderTest();
o.Dispose();
Assert.IsFalse(ObjectHasUndisposedDisposables(o));

我正在考虑使用反射来实现这一点。这似乎是一个相当普遍的要求,但我找不到任何例子。

有人试过吗?

编辑——我不想将一次性用品注入被测 class。

没有通用的方法来处理这个问题。 IDisposeable 接口不需要您跟踪是否调用了 dispose。

我能想到的您可以处理此问题的唯一方法是,如果所有这些一次性 类 使用依赖项注入进行注入。如果你这样做了,你可以模拟注入的 类 并跟踪是否调用了 dispose。

    [TestMethod]
    public void TestAllDisposeablesAreDisposed()
    {
        var fooMock = new Mock<IFoo>();
        fooMock.Setup(x=>x.Dispose())
            .Verifiable("Dispose never called on Foo");

        var barMock = new Mock<IBar>();
        barMock.Setup(x => x.Dispose())
            .Verifiable("Dispose never called on Bar");

        using (var myClass = new MyClass(fooMock.Object, barMock.Object))
        {
        }

        fooMock.Verify();
        barMock.Verify();
    }

您是要测试您的 IDisposable 行为是否在给定的 class 内起作用,还是要测试管理您的一次性 class 的任何东西是否实际调用IDisposable?这是有效的,如果你不确定它是否被调用并且你想确定的话。 (我已经为此编写了测试。)

为此,您可以像这样创建一个简单的 class:

public class DisposeMe : IDisposable
{
    public bool Disposed {get;set;}
    public void Dispose()
    {
        Disposed = true;
    }
}

然后您可以断言 Disposed 为真,而您希望它已被处置。

例如,我在从 DI 容器创建 classes 时使用过它,我想确认当容器创建的 class 被释放时,它是一次性的依赖关系得到处理。我无法在实际的具体 classes 中跟踪它,但我可以测试 DI 容器正在做我认为它应该做的事情。 (这不是我会一遍又一遍地写的测试。例如,一旦我确认了 Windsor 的预期行为,我就不会继续为它编写单元测试。)

如果您不愿意在构造函数中使用依赖注入,因为这会迫使您更改接口,那么您可以通过属性注入依赖。如果没有注入任何东西,请给你的 属性 一个默认值,这样当你使用 class 时,你就不必记住传递真实的实现

例如

public class ObjectUnderTest : IDisposable
{
    private IObjectNeedingDisposal _foo;

    public IObjectNeedingDisposal Foo
    {
        get { return _foo ?? (_foo = new ObjectNeedingDisposal()); }
        set { _foo = value; }
    }

    public void MethodUsingDisposable()
    {
        Foo.DoStuff();
    }

    public void Dispose()
    {
        Foo.Dispose();
    }
}

在您的测试中,您可以创建对象的实例并传入 class 的模拟版本,但在实际实现中,您可以将其保留为未设置,它将默认为类型的实例你想要

在不重构代码的情况下验证您正在寻找的行为的唯一方法是使用代码编织工具,例如; Typemock Isolator、MsFakes 等...

以下代码段展示了使用 MsFakes 验证行为的方法:

[TestMethod]
public void TestMethod1()
{
    var wasCalled = false;
    using (ShimsContext.Create())
    {
        ForMsFakes.Fakes.ShimDependency.AllInstances.Dispose = dependency =>
        {
            wasCalled = true;
        };

        var o = new ObjectUnderTest();

        o.Dispose();
    }

    Assert.IsTrue(wasCalled);
}

public class Dependency : IDisposable
{
    public void Dispose() {}
}

public class ObjectUnderTest: IDisposable
{
    private readonly Dependency _d = new Dependency();

    public void Dispose()
    {
        _d.Dispose();
    }
}