在Xamarin中,NUnit异步测试无法正常工作

4

我是C#的新手,正在尝试评估Xamarin。我一直在制作一个小型的Web API库,目前进展顺利,但现在我正在尝试测试网络组件,遇到了一些问题。

OS:           OS X 10.9.1
Xamarin:      4.2.2 (build 2)
Library:      PCL targeting 4.0 (Profile158)
Nunit target: Mono / .NET 4.5

这个类/方法:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;

namespace LibTest
{
    public class AsyncTest
    {
        public async Task<string> DoTest()
        {
            return await new HttpClient().GetStringAsync( "http://www.reddit.com/.json?limit=1" );
        }
    }
}

NUnit测试类/用例:

using System;
using NUnit.Framework;
using LibTest;

namespace LibTestTests
{
    [TestFixture]
    public class LibTestTest
    {
        [Test]
        public async void TempTest()
        {
            string data = await new AsyncTest().DoTest();

            Assert.IsNotNull( data );
            Assert.IsNull( data );
        }
    }
}

我的其他测试似乎都能正常工作(解析等),但是这个测试却直接跳过,表现得好像测试已经通过了,但是我不明白为什么会这样,因为这些断言是相互矛盾的。
这种情况目前是否不支持,还是我的实现方式有误?
如果我使用ContinueWith()/Wait()使其同步,它就能按预期工作,但显然我不希望在我的库中使用重复的实现来通过测试。
3个回答

9
NUnit支持async Task单元测试;不幸的是,Xamarin使用的是一个旧版本的NUnit,它不支持async Task。然而,我不建议直接在任务上同步阻塞,因为这会使你的错误路径测试变得复杂(异常被包装在AggregateException中)。
我的AsyncEx库中有一个AsyncContext类型,可以这样使用:
[Test]
public void TempTest()
{
    AsyncContext.Run(async () =>
    {
        string data = await new AsyncTest().DoTest();

        Assert.IsNotNull( data );
        Assert.IsNull( data );
    });
}

太棒了,我同意阻塞不是最理想的情况,等我上电脑后一定会查看那个库。 - Josh
所以,我只能在使用异步lambda async () => {}时编译和运行它,但是然后我会得到一个错误:Could not load type 'System.Object' from assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'。我使用Nuget安装了Nitro.AsyncEx,版本为2.1.3`。 - Josh
тЈ»УЃйТў»тЏаСИ║NunitжА╣уЏ«уџёуЏ«ТаЄТАєТъХТў»Mono/.NET 4.5тљЌ№╝Ъ - Josh
抱歉,对于iOS项目,您必须确保包含和编译所引用的dll文件。我在这方面没有太多经验,但这个问题可能会有所帮助 - Stephen Cleary

6
您有一个async void方法。每当您使用它时,这应该是一个很大的红色警告信号。
一旦TempTest命中第一个await,它将返回给调用者。代码的其余部分会运行,但从调用者的角度来看,该方法已经完成,并且调用者无法知道方法何时结束、任何异常抛出、任何断言失败等。
当您同步等待时,您当然确保异常由调用此方法的线程抛出,而不是在某个其他线程的某个其他地方抛出,测试框架也无法观察到它。
要正确测试异步方法,测试框架确实需要被设计为支持它。它应该能够处理返回Task的方法,并根据Task的结果进行操作。如果这不是一个选项,那么您就需要手动完成这项工作。理想情况下,为了保持适当的异步性,您应该设置自己的消息泵(参见这里的示例),该消息泵将能够推送消息,直到顶层操作完成,从而使您能够在同步方法中运行异步代码。

谢谢,那很清楚明白。 - Josh

4
这里的操作顺序如下:
  1. NUnit 运行 TempTest()
  2. TempTest 调用 new AsyncTest().DoTest(),它返回一个任务 (Task)
  3. 任务安排 TempTest 的剩余部分(即断言)作为延续任务
  4. TempTest 方法返回 (void),NUnit 继续执行下一个测试
  5. 延续任务在未来某个时间点执行断言
  6. 由于没有观察到延续任务,断言失败异常未被处理
你可能希望测试是同步的,并且在测试异步事物的结果上阻塞:
[Test]
public void TempTest()
{
    string data = new AsyncTest().DoTest().Result;

    Assert.IsNotNull( data );
    Assert.IsNull( data );
}

啊,这很有道理。虽然不是最理想的,但我想等待可能是保留库异步性的最佳选择。谢谢。 - Josh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接