如何使用“using”语句对方法进行单元测试?

发布于 2024-08-15 09:17:09 字数 345 浏览 5 评论 0原文

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

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

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

我如何测试类似上面的代码?

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

How can I write a unit test for a method that has a using statement?

For example let assume that I have a method Foo.

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

How can I test something like the code above?

Sometimes I choose not to use using statement and Dispose() an object manually. I hope that someone will show me a trick I can use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

无人问我粥可暖 2024-08-22 09:17:09

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

public bool Foo()
{
    using (IMyDisposableClass client = _myDisposableClassFactory.Create())
    {
        return client.SomeOtherMethod();
    }
}

If you construct the IMyDisposableClass using a factory (injected into the parent class) rather than using the new keyword, you can mock the IMyDisposable and do a verify on the dispose method call.

public bool Foo()
{
    using (IMyDisposableClass client = _myDisposableClassFactory.Create())
    {
        return client.SomeOtherMethod();
    }
}
非要怀念 2024-08-22 09:17:09

如果您已经有了代码并询问如何测试它,那么您就没有先编写测试......所以并没有真正进行 TDD。

但是,您这里拥有的是依赖项。因此,TDD 方法是使用依赖注入。使用 IoC 容器(例如 Unity

当“正确”执行 TDD 时,您的思维过程应该在这种情况下按如下方式运行:

  • 我需要执行一个 Foo
  • 为此,我将依赖于一个外部依赖项来实现一个接口(新的或 然后
  • 因此,我将向通过其构造函数声明 Foo 的类中注入一个 IMyDisposableClass

您将编写一个 (或更多)测试失败,然后您才会到达编写 Foo 函数体的位置,并确定是否需要使用 using 块。

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

一旦您确定需要使用 using 块,您就会想要编写一个失败的测试 - 例如使用类似 Rhino Mocks 设置期望,在实现 IMyDisposableClassDispose代码>.

例如(使用 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();
}

If you already have your code and are asking how to test it, then you're not writing your tests first...so aren't really doing TDD.

However, what you have here is a dependency. So the TDD approach would be to use Dependency Injection. This can be made easier using an IoC container like Unity.

When doing TDD "properly", your thought processes should run as follows in this sort of scenario:

  • I need to do a Foo
  • For this I will rely on an external dependency that will implement an interface (new or pre-existing) of IMyDisposableClass
  • Therefore I will inject an IMyDisposableClass into the class in which Foo is declared via its constructor

Then you would write one (or more) tests that fail, and only then would you be at the point where you were writing the Foo function body, and determine whether you needed to use a using block.

In reality you might well know that yes, you will use a using block. But part of the point of TDD is that you don't need to worry about that until you've proven (via tests) that you do need to use an object that requires this.

Once you've determined that you need to use a using block you would then want to write a test that fails - for example using something like Rhino Mocks to set an expectation that Dispose will get called on a mock object that implements IMyDisposableClass.

For example (using Rhino Mocks to mock 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());
    }
}

Class in which your Foo function exists, with IMyDisposableClass injected as a dependency:

public class Stuff
{
    private readonly IMyDisposableClass _client;

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

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

And the interface IMyDisposableClass

public interface IMyDisposableClass : IDisposable
{
    bool SomeOtherMethod();
}
陈甜 2024-08-22 09:17:09

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

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

有时我认为流行语比开发者还多:)

Your question doesn't make sense. If you are using TDD, then you should already have a test for what you have written. Requirements, then tests, then design, then development. Either your code passes your tests, or it doesn't.

Now, if your question is how to unit test the above piece of code, then that's another question completely, and I think the other posters have answered it up there.

Sometimes I think there are more buzzwords than developers :)

浮华 2024-08-22 09:17:09

此类包装方法不可进行单元测试,因为您无法指定相关的前置条件或后置条件。

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

Wrapper methods like that aren't unit-testable, because you can't specify the relevant preconditions or post-conditions.

To make the method testable, you'll have to pass an IMyDisposableClass instance into the method or into the class hosting Foo (and make the host class itself implement IDisposable), so you can use a test double instead of the real thing to verify any interactions with it.

诗笺 2024-08-22 09:17:09

你的问题没有意义。如果您正在进行 TDD,那么您发布的方法已经经过充分测试,否则它根本就不存在。所以,你的问题没有意义。

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

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

Your question doesn't make sense. If you are doing TDD, then the method that you posted is already fully tested, otherwise it couldn't even exist in the first place. So, your question doesn't make sense.

If, on the other hand, the method that you posted does already exist, but isn't fully tested, then you aren't doing TDD anyway, and your question about TDD doesn't make sense, either.

In TDD, it is simply impossible for untested code to exist. Period.

幽蝶幻影 2024-08-22 09:17:09

您还可以更改方法签名以允许传递模拟以进行单元测试。这将提供使用工厂的替代方案,工厂也需要进行单元测试。在这里,直接调用方法而不是类构造函数可能更可取。

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

You could also change the method signature to allow passing in a mock for unit testing. This would provide an alternative to using a factory, which would also need to be unit tested. DI into the method as opposed to the class constructor may be preferable here.

public bool Foo(IMyDisposableClass mock = null)
{
    using (IMyDisposableClass client = mock ?? new MyDisposableClass())
    {
        return client.SomeOtherMethod();
    }
}
你另情深 2024-08-22 09:17:09

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

如果您想测试 MyDisposableClass' dispose 方法以查看它是否正常工作,那么这应该是针对 MyDisposableClass 构建的单独单元测试。

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

If you are testing Foo, then you should be looking at the output of Foo, not worrying about the disposal of the class it is using internally.

If you want to test MyDisposableClass' dispose method to see if it is working, that should be a separate unit test built against MyDisposableClass.

You don't need to unit test the using { } block, since that is part of the language. You either trust that it's working, or don't use C#. :) I don't see the need to write a unit test to verify that Dispose() is being called.

生来就爱笑 2024-08-22 09:17:09

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

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

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

这个问题我想其他博主已经回答过了,我就不再赘述了。

Without a specification for Foo, how can we say how to test it?

  1. Get the specification for Foo.
  2. Write tests to ensure that it meets all specifications and requirements (or a reasonable subset- some functions could require practically infinite amounts of data to test).

I believe you have a second, implicit question in there - which is how to test that use of your MyDisposableClass correctly Disposes of the object when it is freed by exiting an using clause. This is a separate test issue, and shouldn't be combined with the test of Foo, since the specification of Foo shouldn't reference implementation specific details such as the use of your MyDisposabeClass.

I think the other posters have answered this question, so I won't further elaborate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文