单元测试异步代码

时间:2015-11-21 08:38:36

标签: c# .net unit-testing asynchronous moq

我有简单的类,使用这样的简单方法:

partial class SimpleClass
{
    private readonly ISimpleManager _simpleManager;
    public SimpleClass(ISimpleManager simpleManager)
    {
        _simpleManager = simpleManager;
    }

    public async void SimpleMethod()
    {
        IsInProgress = true;
        DoSomeWork();
        Task<int> hardWork0Task = _simpleManager.DoHardWork0Async();
        Task<int> hardWork1Task = _simpleManager.DoHardWork1Async();
        DoIndependentWork();
        int hardWork0Result = await hardWork0Task.ConfigureAwait(false);
        DoDependentWork(hardWork0Result);
        int hardWork1Result = await hardWork1Task.ConfigureAwait(false);
        DoDependentWork(hardWork1Result);
        IsInProgress = false;
    }
}

假设属性IsInProgress只是bool属性,通知GUI关于其状态以允许刷新进度条。 DoSomeWorkDoDependentWorkDoIndependentWork是使用或不使用辛勤工作结果的一些方法。

在这种情况下,

ISimpleManager是您可以想象的最简单的界面:

interface ISimpleManager
{
    Task<int> DoHardWork0Async();
    Task<int> DoHardWork1Async();
}

我必须使用Moq和NUnit编写一些单元测试。如何为这种情况编写单元测试?我想检查在与GUI异步运行的整个代码期间IsInProgress属性的状态是否未更改为false。是否有意义?可能吗?如果我的async方法返回Task或通用Task<T>怎么办?如果我为true配置等待

,该怎么办?

2 个答案:

答案 0 :(得分:1)

您只需要控制ISimpleManager任务的完成时间。根据Ilya的回答,您可以使用SemaphoreSlim执行此操作,也可以直接使用TaskCompletionSource<T>。我通常更喜欢TaskCompletionSource<T>,因为它更简单;但是,SemaphoreSlim实例可以重复使用,而TaskCompletionSource<T>只能触发一次。

另外,除非方法是事件处理程序,否则应避免使用async void应该是&#34;默认&#34;任何形式 - 默认应该是返回Task,除非您绝对不能。因此,在此示例中,SimpleMethod肯定会返回Task

以下是使用TCS的样子:

async Task MyTestMethod()
{
  // Set up the mock object
  var tcs0 = new TaskCompletionSource<int>();
  var tcs1 = new TaskCompletionSource<int>();
  var stub = new Mock<ISimpleManager>();
  stub.Setup(x => x.DoHardWork0Async()).Returns(tcs0.Task);
  stub.Setup(x => x.DoHardWork1Async()).Returns(tcs1.Task);

  var sut = new SimpleClass(stub.Object);
  var task = sut.SimpleMethod();

  Assert.True(sut.InProgress);
  tcs0.SetResult(7);
  Assert.True(sut.InProgress);
  tcs1.SetResult(13);
  await task;
  Assert.False(sut.InProgress);
}

答案 1 :(得分:0)

以下是如何对IsInProgress设置正确的单元测试:

  1. 创建一个等待的信号量:

    var sem = new SemaphoreSlim(1);
    
  2. 使用以下实现创建存根ISimpleManager

    async Task<int> DoHardWork0Async() {
       await sem.WaitAsync();
       return 0;
    }
    
  3. 致电SimpleMethod。在DoHardWork0Async

  4. 的调用中,它会被困在sempahore上
  5. 验证IsInProgress

  6. 的效果
  7. 作为清理步骤,释放信号量:

    sem.Release();