C# 如何使用xUnit测试包含wait的异步void DelegateCommand方法?
这是我第一次为异步方法编写单元测试。我正在使用xUnit。我搜索了所以没有结果。我发现最好的方法是实现示例中的C# 如何使用xUnit测试包含wait的异步void DelegateCommand方法?,c#,async-await,xunit,delegatecommand,C#,Async Await,Xunit,Delegatecommand,这是我第一次为异步方法编写单元测试。我正在使用xUnit。我搜索了所以没有结果。我发现最好的方法是实现示例中的IAsyncLifetime,但对我来说并不管用。我将感谢任何提示如何解决这个问题 目前我所拥有的。在测试的VM中,我有一个命令: public ICommand testresultcommand{get;private set;} 命令在VM构造函数中初始化,如下所示: testresultcommand=newdelegateCommand(OnTestResultExecuteA
IAsyncLifetime
,但对我来说并不管用。我将感谢任何提示如何解决这个问题
目前我所拥有的。在测试的VM中,我有一个命令:
public ICommand testresultcommand{get;private set;}
命令在VM构造函数中初始化,如下所示:
testresultcommand=newdelegateCommand(OnTestResultExecuteAsync)代码>
命令调用方法:
private async void OnTestResultExecuteAsync(object obj)
{
TokenSource = new CancellationTokenSource();
CancellationToken = TokenSource.Token;
await TestHistoricalResultsAsync();
}
TestHistoricalResultsAsync方法的签名如下:
专用异步任务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:对象引用未设置为
对象的实例
异常的堆栈跟踪是:
提前感谢您的时间和建议。其中一个原因是它们很难测试。对于您的问题,大多数开发人员都会执行以下操作之一:
定义并使用IAsyncCommand
接口
将其逻辑异步任务公开
使用支持异步命令的框架,例如MvvmCross
有关详细信息,请参阅我的
下面是第二种方法的示例:
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);
}
如果您不想仅仅为了单元测试而公开异步任务
方法,那么您可以使用某种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);
}
如果您更喜欢使用imvxancommand
方法,但不希望使用MvvmCross-dependency,则不难实现。其中一个原因是它们很难测试。对于您的问题,大多数开发人员都会执行以下操作之一:
定义并使用IAsyncCommand
接口
将其逻辑异步任务公开
使用支持异步命令的框架,例如MvvmCross
有关详细信息,请参阅我的
下面是第二种方法的示例:
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);
}
如果您不想仅仅为了单元测试而公开异步任务
方法,那么您可以使用某种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);
}
如果您喜欢使用imvxancommand
方法,但不希望使用MvvmCross-dependency,则不难做到这一点。async void
应仅用于事件处理程序。此外,代码应该被重构为更可靠的代码,以便您能够正确地测试和维护代码。如果无法更好地查看被测试的类,则无法提供更多建议,因为您似乎正在尝试测试私有成员。@Nkosi但是async void
不应也用于DelegateCommand
?@Nkosi好吧,在我的测试方法中,我刚刚执行了command asyncwait Task.Run()=>_viewModel.testresultcommand.Execute(null))代码>成功了。基于此的解决方案:async void
应仅用于事件处理程序。此外,代码应该被重构为更可靠的代码,以便您能够正确地测试和维护代码。如果无法更好地查看被测试的类,则无法提供更多建议,因为您似乎正在尝试测试私有成员。@Nkosi但是async void
不应也用于DelegateCommand
?@Nkosi好吧,在我的测试方法中,我刚刚执行了command asyncwait Task.Run()=>_viewModel.testresultcommand.Execute(null))代码>成功了。基于此的解决方案:好的,谢谢您的反馈。它给了我大局。我必须修改我的DelegateCommand
以使用testresultcommand=newdelegateCommand(async()=>await-OnTestResultExecuteAsync())代码>。我以前尝试过这个,但是DelegateCommand
不想接受参数,所以我选择了void。我来换这个。非常感谢。您好,我再次测试了您的方法,包括创建AsyncCommand
,但使用的是测试方法,而不是wait\u viewModel.OnTestResultExecuteAsync()代码>我必须使用wait Task.Run(()=>_viewModel.testresultcommand.Execute(null))
,否则可能是TestHistoricalResultsAsync
不是异步运行的,仍然会删除相同的异常。等待任务。Run
可能无法修复此问题。听起来你有一个竞赛条件,而任务.Run
使它(几乎)花费了足够长的时间。一个更好的解决方案是修复竞争条件。老实说,等待任务。运行
修复此问题TestHistoricalResultsAsync
仅返回任务
,并使用等待任务。Run
异步执行,不会引发异常。你可以在这里找到源代码:我不确定你所说的修复竞争条件是什么意思?好的,谢谢你的反馈。它给了我大局。我必须修改我的DelegateCommand
以使用testresultcommand=newdelegateCommand(async()=>await-OnTestResultExecuteAsync())代码>。我以前尝试过这个,但是DelegateCommand
不想接受参数,所以我选择了void。我来换这个。非常感谢。您好,我再次测试了您的方法,包括创建AsyncCommand
,但使用的是测试方法,而不是wait\u viewModel.OnTestResultExecuteAsync()代码>我必须使用wait Task.Run(()=>_viewModel.testresultcommand.Execute(null))
,否则似乎是TestHistoricalResultsAsync
不是异步运行的,并且仍然会删除