【问题标题】:How to test an async void method如何测试异步 void 方法
【发布时间】:2013-03-21 18:28:23
【问题描述】:

请考虑如下所示的代码。通过调用 GetBrands,将为属性 Brands 分配适当的数据。

public class BrandsViewModel : ViewModelBase
{
    private IEnumerable<Brand> _brands;
    public IEnumerable<Brand> Brands
    {
        get { return _brands; }
        set { SetProperty(ref _brands, value); }
    }

    public async void GetBrands()
    {
        // ......

        Brands = await _dataHelper.GetFavoriteBrands();

        // ......
    }
}

但是如果我按如下所示进行测试,则测试失败。如何等待 GetBrands 方法中的异步调用?

[TestMethod]
public void AllBrandsTest()
{
    BrandsViewModel viewModel = new BrandsViewModel();
    viewModel.GetBrands();
    Assert.IsTrue(viewModel.Brands.Any());
}

【问题讨论】:

  • 请阅读/查看这个:blogs.msdn.com/b/lucian/archive/2013/02/18/… 它基本上说:除了事件处理程序,不要使用async void
  • @DanielHilgarth 谢谢你,丹尼尔。我重构了我的代码,它就像一个魅力。

标签: c# unit-testing async-await


【解决方案1】:

这里的简单答案是:不要将其设为async void。事实上,不要永远将某些东西设为async void,除非它绝对必须用作事件处理程序。 async void 丢失的东西正是你想要在这里进行测试(并且可能是你的真实代码)的东西。

改为使用async Task 方法,您现在可以等待完成(超时)/添加延续,并检查它是成功退出还是异常退出。

这是一个单词的变化,改为:

public async Task GetBrands()
{
    // ......

    Brands = await _dataHelper.GetFavoriteBrands();

    // ......
}

然后在测试中:

[TestMethod]
public async Task AllBrandsTest()
{
    BrandsViewModel viewModel = new BrandsViewModel();
    var task = viewModel.GetBrands();
    Assert.IsTrue(task.Wait(YOUR_TIMEOUT), "failed to load in time");
    Assert.IsTrue(viewModel.Brands.Any(), "no brands");
}

【讨论】:

  • 如果不使用await,为什么还要保留测试async
  • @svick 这是 OP 的问题,他也没有 await :)
  • @svick 我的错。我已经更正了我的测试代码。如果方法中没有等待,则不需要保持异步。
【解决方案2】:

您的模型(DTO)正在填充自身(数据访问)。这对一个班级来说太多了。通常当你问自己“我到底要如何测试这个”时,是时候进行重构了。创建一个单独的数据访问类:

BrandsViewModel viewModel = new BrandsViewModel();
var brandAccess = new BrandsDataAccess();
viewModel.Brands = await brandAccess.GetAllBrands();
Assert.IsTrue(viewModel.Brands.Any());

现在你可以测试BrandsDataAccess.GetAllBrands()了。

【讨论】:

  • async 去哪儿了?我的意思是:原始代码是async,你的不是,所以我认为它不是一个好的替代品。
  • @svick 重要的是原则,而不是实施。见编辑。
  • @CodeCaster 你能进一步解释一下你的观点吗?就单一职责原则而言,我认为在同一类中填充自己的属性是可以接受的。不是吗?
  • @Shinbo 不,数据 transfer 对象((视图)模型是)不应该与数据 access 相关。
  • @CodeCaster 感谢您的澄清。如果 DTO 包含数据访问逻辑,会有什么副作用?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
  • 1970-01-01
  • 2013-02-15
  • 2021-02-18
  • 1970-01-01
  • 2014-01-04
相关资源
最近更新 更多