0

这是我第一次为异步方法编写单元测试。我正在使用xUnit。我搜索了所以没有有希望的结果。我发现但对我没有用的最好的方法是IAsyncLifetime这个例子中实现。对于如何解决此问题的任何提示,我将不胜感激。

目前我所拥有的。在经过测试的虚拟机中,我有一个命令:

public ICommand TestResultsCommand { get; private set; }

该命令在 VM 构造函数中初始化,如下所示:

TestResultsCommand = new DelegateCommand(OnTestResultExecuteAsync);

命令调用方法:

private async void OnTestResultExecuteAsync(object obj)
        {
            TokenSource = new CancellationTokenSource();
            CancellationToken = TokenSource.Token;

            await TestHistoricalResultsAsync();
        }

TestHistoricalResultsAsync方法的签名如下:

private async Task TestHistoricalResultsAsync()

现在让我们进入单元测试项目。目前在测试类我有一个方法:

[Fact]//testing async void
        public void OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            CancellationToken cancellationToken = tokenSource.Token;
            _viewModel.TestResultsCommand.Execute(null);
            Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
            Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
        }

测试让我例外:

消息:System.NullReferenceException:对象引用未设置为对象的实例。

异常的堆栈跟踪是: 在此处输入图像描述

提前感谢您的时间和建议。

4

1 回答 1

2

方法问题之一async void是它们难以测试。对于您的问题,大多数开发人员会执行以下操作之一:

  1. 定义和使用IAsyncCommand接口。
  2. 使他们的逻辑async Task和公开。
  3. 使用支持异步命令的框架,例如 MvvmCross。

有关详细信息,请参阅我的MSDN 杂志文章

这是第二种方法的示例:

TestResultsCommand = new DelegateCommand(async () => await OnTestResultExecuteAsync());

public async Task OnTestResultExecuteAsync()
{
  TokenSource = new CancellationTokenSource();
  CancellationToken = TokenSource.Token;

  await TestHistoricalResultsAsync();
}

[Fact]
public async Task OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
{
  CancellationTokenSource tokenSource = new CancellationTokenSource();
  CancellationToken cancellationToken = tokenSource.Token;
  await _viewModel.OnTestResultExecuteAsync();
  Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
  Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
}

如果您不想async Task仅仅为了单元测试而公开方法,那么您可以使用IAsyncCommand某种方法;可以是您自己的(如我的文章中所详述)或来自库(例如 MvvmCross)的一个。这是一个使用 MvvmCross 类型的示例:

public IMvxAsyncCommand TestResultsCommand { get; private set; }

TestResultsCommand = new MvxAsyncCommand(OnTestResultExecuteAsync);

private async Task OnTestResultExecuteAsync() // back to private
{
  TokenSource = new CancellationTokenSource();
  CancellationToken = TokenSource.Token;

  await TestHistoricalResultsAsync();
}

[Fact]
public async Task OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
{
  CancellationTokenSource tokenSource = new CancellationTokenSource();
  CancellationToken cancellationToken = tokenSource.Token;
  await _viewModel.TestResultsCommand.ExecuteAsync();
  Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
  Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
}

如果您更喜欢这种IMvxAsyncCommand方法但不想要 MvvmCross 依赖项,那么定义自己的IAsyncCommandAsyncCommandtypes并不难。

于 2019-06-13T14:18:07.390 回答