Dispose方法应该进行单元测试吗?

sbe*_*rli 17 c# unit-testing dispose

我正在使用C#.是否建议单元测试处理方法?如果是这样,为什么要测试这些方法呢?

Igo*_*aka 9

是的,但可能很难.Dispose实施中通常会发生两件事:

非托管资源已发布.

在这种情况下,很难验证代码调用,例如Marshal.Release.一种可能的解决方案是注入一个可以进行处理的对象,并在测试期间将模拟传递给它.这样的效果:

interface ComObjectReleaser {
    public virtual Release (IntPtr obj) {
       Marshal.Release(obj);
    }
}

class ClassWithComObject : IDisposable {

    public ClassWithComObject (ComObjectReleaser releaser) {
       m_releaser = releaser;
    }

    // Create an int object
    ComObjectReleaser m_releaser;
    int obj = 1;
    IntPtr m_pointer = Marshal.GetIUnknownForObject(obj);

    public void Dispose() {
      m_releaser.Release(m_pointer);
    }
}

//Using MOQ - the best mocking framework :)))
class ClassWithComObjectTest {

    public DisposeShouldReleaseComObject() {
       var releaserMock = new Mock<ComObjectReleaser>();
       var target = new ClassWithComObject(releaserMock);
       target.Dispose();
       releaserMock.Verify(r=>r.Dispose());
    }
}
Run Code Online (Sandbox Code Playgroud)

Dispose调用其他类的方法

对此的解决方案可能不像上面那么简单.在大多数情况下,Dispose的实现不是虚拟的,因此嘲笑它很难.

一种方法是将这些其他对象包装在一个可模拟的包装器中,类似于System.Web.Abstractions命名空间对HttpContext类的操作 - 即HttpContextBase使用所有虚拟方法定义类,只需将方法调用委托给真实HttpContext类.

有关如何执行此类操作的更多想法,请查看System.IO.Abstractions项目.


Jes*_*cer 8

当然不会伤害。客户代码可能会在处置完您的类的对象后尝试使用它。如果您的类是由其他IDisposable对象组成的,ObjectDisposedException则如果该异常处于不再可用的状态,则应始终抛出该异常。

当然,您应该只测试对象的外部状态。在下面的示例中,我将属性设置为Disposed外部以提供状态。

考虑:

internal class CanBeDisposed : IDisposable
{
    private bool disposed;
    public bool Disposed
    {
        get
        {
            if (!this.disposed)
                return this.disposed;
            throw new ObjectDisposedException("CanBeDisposed");
        }
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                //// Dispose of managed resources.
            }
            //// Dispose of unmanaged resources.
            this.disposed = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,我将如何进行测试:

CanBeDisposed cbd;

using (cbd = new CanBeDisposed())
{
    Debug.Assert(!cbd.Disposed); // Best not be disposed yet.
}

try
{
    Debug.Assert(cbd.Disposed); // Expecting an exception.
}
catch (Exception ex)
{
    Debug.Assert(ex is ObjectDisposedException); // Better be the right one.
}
Run Code Online (Sandbox Code Playgroud)

  • 我看不到您已经证明发生了任何有用的事情。为了值得,您需要检查资源是否实际释放。如果要访问文件,则应检查以确保现在可以打开该文件进行修改。我不确定如何测试数据库连接实际上已关闭。 (2认同)

Nea*_*eal 6

如果你的类创建并使用非托管资源,那么你绝对应该确保Dispose按照你的预期工作 - 虽然可以认为它更像是一个集成测试,因为你需要跳转的类型通过.

如果您的类只创建/使用托管资源(即它们实现IDisposable),那么您真正需要确保的是在正确的时间调用这些资源上的Dispose方法 - 如果您使用某种形式的DI,那么您可以注入模拟并断言已调用Dispose.

看看你的处置方法的复杂性 - 如果它们只有几行,可能只有1个条件,那么问问自己,单元测试它们是否真的有好处.