微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 传递异步操作时出现意外的行为

我非常熟悉异步/等待模式,但我碰到了一些令我奇怪的行为.我相信有一个完全有效的原因,为什么会发生,我很乐意了解行为.

这里的背景是我正在开发一个Windows Store应用程序,由于我是一个谨慎,认真的开发人员,所以我单位测试一切.我很快发现,WSAs不存在ExpectedExceptionAttribute.奇怪,对吧?好吧,没问题!我可以使用扩展方法来复制行为.所以我写道:

public static class TestHelpers
{
    // There's no ExpectedExceptionAttribute for Windows Store apps! Why must Microsoft make my life so hard?!
    public static void AssertThrowsExpectedException<T>(this Action a) where T : Exception
    {
        try
        {
            a();
        }
        catch (T)
        {
            return;
        }

        Assert.Fail("The expected exception was not thrown");
    }
}

而且,它的作品精美.

所以我继续愉快地写我的单元测试,直到我打了一个异步方法,我想确认在某些情况下抛出异常. “没问题,”我想到自己,“我可以通过一个异步的lambda!”

所以我写了这个测试方法

[TestMethod]
public async Task Network_Interface_Being_Unavailable_Throws_Exception()
{
    var webManager = new FakeWebManager
    {
        IsNetworkAvailable = false
    };

    var am = new AuthenticationManager(webManager);
    Action authenticate = async () => await am.Authenticate("foo","bar");
    authenticate.AssertThrowsExpectedException<LoginFailedException>();
}

令人惊讶的是,这会引发运行时错误.它实际上崩溃了测试者!

我重载了我的AssertThrowsExpectedException方法

public static async Task AssertThrowsExpectedException<TException>(this Func<Task> a) where TException : Exception
{
    try
    {
        await a();
    }
    catch (TException)
    {
        return;
    }

    Assert.Fail("The expected exception was not thrown");
}

我调整了我的测试:

[TestMethod]
public async Task Network_Interface_Being_Unavailable_Throws_Exception()
{
    var webManager = new FakeWebManager
    {
        IsNetworkAvailable = false
    };

    var am = new AuthenticationManager(webManager);
    Func<Task> authenticate = async () => await am.Authenticate("foo","bar");
    await authenticate.AssertThrowsExpectedException<LoginFailedException>();
}

我的解决方案很好,我只是想知道为什么一切都变成梨形,当我尝试调用异步Action.我猜是因为,就运行时而言,这不是一个Action,我只是把lambda加入到它里面.我知道lambda将很乐意分配给Action或Func< Task>.

解决方法

在第二个代码片段的情况下,测试人员可能会崩溃并不奇怪:
Action authenticate = async () => await am.Authenticate("foo","bar");
authenticate.AssertThrowsExpectedException<LoginFailedException>();

当你打电话时,这实际上是一个火灾忘记的an async void method

try
{
    a();
}

a()立即返回,AssertThrowsExpectedException方法也是如此.同时,一些活动在am.Athenticate内可以在后台继续执行,可能在一个池线程上.究竟发生了什么取决于am.Authenticate的实现,但是当这样的异步操作完成并且它抛出LoginFailedException时,它可能会使您的测试程序崩溃.我不知道单元测试执行环境的同步上下文是什么,但是如果使用认的SynchronizationContext,那么在这种情况下,异常可能会被抛出不同的线程.

VS2012自动支持异步单元测试,只要测试方法签名是异步任务即可.所以,我想你已经回答了你自己的问题,使用await和Func< T>为你的测试.

原文地址:https://www.jb51.cc/csharp/95826.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐