void异步方法的断言

时间:2013-11-27 17:47:36

标签: c# testing

我有一个类似的课程:

public class MyClass
{
    public Task MyMethod()
    {
        //do something
    }
}

如上所述,'MyMethod'是异步的,为简单起见,这就是它的实现:

    public Task MyMethod()
    {
        return Task.Run(() =>
        {
            var success = _someService.DoSomething();
            if (!success) throw new Exception("unsuccsesfull");
        });

    }

显然,当MyMethod等待回调将运行时,意味着在正在运行的线程中没有抛出任何异常,这意味着“MyMethod”调用成功。

我想测试一下这个方法。测试方法如下:

[Test]
public async void TestMyMethod_TestInitialState_TestExpectedResult()
{
    // test initialization..
    //...
    //..
    var myClass = new MyClass();
    await myClass.MyMethod();

    Assert.That(????)
}

我的问题 - 什么是正确的断言?

我有一个可能的解决方案 - 将一个逻辑成员添加到'MyClass'并根据方法结果更新该成员:

public class MyClass
{
    public bool SomeMember { get; private set; }

    public Task MyMethod()
    {
        Task.Run(() =>
        {               
            SomeMember = _someService.DoSomething();
            if (!SomeMember ) throw new Exception("unsuccsesfull");
        });

    }
}

这样我可以像这样断言测试:

    [Test]
    public async void TestMyMethod_TestInitialState_SomeMemberShouldBeTrue()
    {
        // test initialization..
        //...
        //..
        var myClass = new MyClass();
        await myClass.MyMethod();

        Assert.True(SomeMember)
    }
}

但是,我不喜欢这个解决方案,因为我正在为'MyClass'添加一个属性,只是为了能够断言测试,我真的不需要在我的商业世界中使用这个属性。此外,添加到类中的每个属性都代表此类的某些状态,并增加了一定程度的复杂性。

建议?

盖。

1 个答案:

答案 0 :(得分:3)

你想要的是“模拟”或“存根”。我们的想法是你重构代码,使它依赖于接口,然后在测试时模拟接口。

有各种框架/工具可以帮助进行模拟(Moq,Microsoft Fakes,TypeMock Isolator,JustMock等),并且还有许多框架可以帮助解决依赖注入密切相关的问题(Unity,Castle Windsor) ,StructureMap,Autofac等)。

但你可以自己开始做。首先,重构MyClass因此取决于ISomeService

public interface ISomeService
{
  bool DoSomething();
}

public class MyClass
{
  private readonly ISomeService _someService;

  public MyClass(ISomeService someService)
  {
    _someService = someService;
  }

  public Task MyMethod()
  {
    return Task.Run(() =>
    {
      var success = _someService.DoSomething();
      if (!success) throw new Exception("unsuccsesfull");
    });
  }
}

然后在你的单元测试中:

private class TestService : ISomeService
{
  public bool DoSomethingReturnValue { get; set; }

  public bool DoSomething() { return DoSomethingReturnValue; }
}

[Test]
public async Task TestMyMethod_TestInitialState_TestExpectedResult()
{
  var myClass = new MyClass(new TestService { DoSomethingReturnValue = true });
  await myClass.MyMethod();
}

[Test]
public async Task TestMyMethod_TestInitialState_TestFailure()
{
  var myClass = new MyClass(new TestService { DoSomethingReturnValue = false });
  Assert.Throws(() => myClass.MyMethod()); // (I'm unsure of the exact NUnit syntax)
}