如何使用 xUnit 测试包含 await 的 async void DelegateCommand 方法?
How to test async void DelegateCommand method containing await with xUnit?
这是我第一次为异步方法编写单元测试。我正在使用 xUnit。我搜索了 SO,但没有得到满意的结果。我发现但对我没有用的最好方法是从 THIS 示例中实现 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()
现在让我们进入单元测试项目。目前正在测试 class 我有一个方法:
[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);
}
测试给我一个例外:
Message: System.NullReferenceException : Object reference not set to
an instance of an object.
异常的堆栈跟踪是:
提前感谢您的宝贵时间和建议。
其中一个 problems of async void
methods 是它们很难测试。对于您的问题,大多数开发人员会执行以下操作之一:
- 定义并使用
IAsyncCommand
接口。
- 使他们的逻辑
async Task
和public。
- 使用支持异步命令的框架,例如 MvvmCross。
有关详细信息,请参阅我的 MSDN magazine article on the subject。
这是第二种方法的示例:
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 依赖项,那么 define your own IAsyncCommand
and AsyncCommand
types.
并不难
这是我第一次为异步方法编写单元测试。我正在使用 xUnit。我搜索了 SO,但没有得到满意的结果。我发现但对我没有用的最好方法是从 THIS 示例中实现 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()
现在让我们进入单元测试项目。目前正在测试 class 我有一个方法:
[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);
}
测试给我一个例外:
Message: System.NullReferenceException : Object reference not set to an instance of an object.
异常的堆栈跟踪是:
提前感谢您的宝贵时间和建议。
其中一个 problems of async void
methods 是它们很难测试。对于您的问题,大多数开发人员会执行以下操作之一:
- 定义并使用
IAsyncCommand
接口。 - 使他们的逻辑
async Task
和public。 - 使用支持异步命令的框架,例如 MvvmCross。
有关详细信息,请参阅我的 MSDN magazine article on the subject。
这是第二种方法的示例:
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 依赖项,那么 define your own IAsyncCommand
and AsyncCommand
types.