18

如何为具有 using 语句的方法编写单元测试?

例如,假设我有一个方法Foo

public bool Foo()
{
    using (IMyDisposableClass client = new MyDisposableClass())
    {
        return client.SomeOtherMethod();
    }
}

我怎样才能测试类似上面的代码?

有时我选择不手动使用using语句和对象。Dispose()我希望有人能告诉我一个我可以使用的技巧。

4

8 回答 8

22

如果您IMyDisposableClass使用工厂(注入到父类中)而不是使用 new 关键字来构建,您可以模拟IMyDisposable并在 dispose 方法调用上进行验证。

public bool Foo()
{
    using (IMyDisposableClass client = _myDisposableClassFactory.Create())
    {
        return client.SomeOtherMethod();
    }
}
于 2009-12-23T16:08:17.907 回答
18

如果您已经有了自己的代码并且正在询问如何对其进行测试,那么您就不会先编写测试......所以并不是真的在做 TDD。

但是,您在这里拥有的是依赖项。所以 TDD 方法是使用Dependency Injection使用像Unity这样的IoC容器可以使这更容易。

在“正确”地进行 TDD 时,您的思维过程应该在这种情况下运行如下:

  • 我需要做一个Foo
  • 为此,我将依赖一个外部依赖项,该依赖项将实现一个接口(新的或预先存在的)IMyDisposableClass
  • 因此,我将在通过其构造函数声明IMyDisposableClass的类中注入一个Foo

然后你会编写一个(或多个)失败的测试,然后你才会在你编写Foo函数体的地方,并确定你是否需要使用一个using块。

实际上,您可能很清楚,是的,您将使用一个using块。但是 TDD 的部分意义在于,在您(通过测试)证明您确实需要使用需要此功能的对象之前,您无需担心这一点。

一旦你确定你需要使用一个using块,你就会想要编写一个失败的测试——例如使用Rhino Mocks 之类的东西来设置一个期望,该期望Dispose将在一个实现IMyDisposableClass.

例如(使用 Rhino Mocks 模拟IMyDisposableClass)。

[TestFixture]
public class When_calling_Foo
{
    [Test]
    public void Should_call_Dispose()
    {
        IMyDisposableClass disposable = MockRepository
                                        .GenerateMock<IMyDisposableClass>();

        Stuff stuff = new Stuff(disposable);

        stuff.Foo();

        disposable.AssertWasCalled(x => x.Dispose());
    }
}

Foo 函数所在的类,IMyDisposableClass作为依赖注入:

public class Stuff
{
    private readonly IMyDisposableClass _client;

    public Stuff(IMyDisposableClass client)
    {
        _client = client;
    }

    public bool Foo()
    {
        using (_client)
        {
            return _client.SomeOtherMethod();
        }
    }
}

还有界面IMyDisposableClass

public interface IMyDisposableClass : IDisposable
{
    bool SomeOtherMethod();
}
于 2009-12-23T16:17:07.900 回答
6

你的问题没有意义。如果您使用的是 TDD,那么您应该已经对您所写的内容进行了测试。需求,然后是测试,然后是设计,然后是开发。您的代码要么通过测试,要么不通过。

现在,如果您的问题是如何对上面的代码进行单元测试,那么这完全是另一个问题,我认为其他发帖人已经回答了。

有时我认为流行语比开发人员多:)

于 2009-12-23T16:20:51.357 回答
2

像这样的包装器方法不是可单元测试的,因为您无法指定相关的前置条件或后置条件。

为了使方法可测试,您必须将IMyDisposableClass实例传递给方法或托管类Foo(并使宿主类本身实现IDisposable),因此您可以使用测试替身而不是真实的东西来验证与它的任何交互.

于 2009-12-23T16:09:09.747 回答
0

你的问题没有意义。如果您正在做 TDD,那么您发布的方法已经经过全面测试,否则它甚至根本不存在。所以,你的问题没有意义。

另一方面,如果您发布的方法确实已经存在,但没有经过全面测试,那么您无论如何都没有进行 TDD,并且您关于 TDD 的问题也没有任何意义。

在 TDD 中,根本不可能存在未经测试的代码。时期。

于 2009-12-23T17:07:59.570 回答
0

您还可以更改方法签名以允许传递模拟以进行单元测试。这将提供使用工厂的替代方法,工厂也需要进行单元测试。与类构造函数相对的方法中的 DI 在这里可能更可取。

public bool Foo(IMyDisposableClass mock = null)
{
    using (IMyDisposableClass client = mock ?? new MyDisposableClass())
    {
        return client.SomeOtherMethod();
    }
}
于 2022-02-09T15:39:01.857 回答
-1

如果您正在测试 Foo,那么您应该查看 Foo 的输出,而不用担心它在内部使用的类的处置。

如果您想测试MyDisposableClass' dispose 方法以查看它是否正常工作,那应该是针对MyDisposableClass.

您不需要对using { }块进行单元测试,因为这是语言的一部分。您要么相信它正在工作,要么不使用 C#。:) 我认为不需要编写单元测试来验证Dispose()是否被调用。

于 2009-12-23T16:11:35.013 回答
-1

如果没有 Foo 的规范,我们怎么能说如何测试呢?

  1. 获取 Foo 的规范。
  2. 编写测试以确保它满足所有规范和要求(或合理的子集——某些功能可能需要几乎无限量的数据来测试)。

我相信你有第二个隐含的问题 - 这是如何测试 MyDisposableClass 在通过退出 using 子句释放对象时正确处理对象的使用。这是一个单独的测试问题,不应与 Foo 的测试相结合,因为 Foo 的规范不应引用实现特定的细节,例如 MyDisposabeClass 的使用。

我想其他的发帖者已经回答了这个问题,所以我不会进一步详细说明。

于 2009-12-24T02:46:05.963 回答