C# 第二次验证时未执行Moq调用
我正在使用NUnit和Moq测试一些简单的类 当我运行测试时,第二次moq验证检查失败。奇怪的是,我在一个类似的班级里做了完全相同的事情,效果很好。我寻找过类似的问题,但似乎只找到了返回异步任务的答案 如果我交换在实际类中测试的方法调用的顺序,以便首先调用PushAsync方法,那么失败的是PostStats验证。 这会让我觉得它只能进行一次验证,但在我的另一个测试中,我已经完成了两次验证(都使用相同的返回类型) 我得到的错误是:C# 第二次验证时未执行Moq调用,c#,asynchronous,xamarin.forms,nunit,moq,C#,Asynchronous,Xamarin.forms,Nunit,Moq,我正在使用NUnit和Moq测试一些简单的类 当我运行测试时,第二次moq验证检查失败。奇怪的是,我在一个类似的班级里做了完全相同的事情,效果很好。我寻找过类似的问题,但似乎只找到了返回异步任务的答案 如果我交换在实际类中测试的方法调用的顺序,以便首先调用PushAsync方法,那么失败的是PostStats验证。 这会让我觉得它只能进行一次验证,但在我的另一个测试中,我已经完成了两次验证(都使用相同的返回类型) 我得到的错误是: Message: Moq.MockException : Ex
Message: Moq.MockException :
Expected invocation on the mock at least once, but was never performed: p => p.PushAsync(It.IsAny<ExerciseNotes>())
Configured setups:
IPageService p => p.PushAsync(It.IsAny<ExerciseNotes>())
No invocations performed
这是我的测试课
class ExerciseRatingViewModelTests
{
private ExerciseRatingViewModel _exerciseRatingViewModel;
private Mock<IPageService> _pageService;
private Mock<IDataService> _dataService;
private Mock<IAPIService> _apiService;
[SetUp]
public void Setup()
{
_pageService = new Mock<IPageService>();
_dataService = new Mock<IDataService>();
_apiService = new Mock<IAPIService>();
int sequenceID = 1;
_exerciseRatingViewModel = new ExerciseRatingViewModel(sequenceID, _pageService.Object, _dataService.Object, _apiService.Object);
}
[Test()]
public void SubmitStats_WhenTouched_ShouldNavigateToNotesPage()
{
//Arrange
_apiService.Setup(a => a.PostStats(It.IsAny<SequenceRating>())).ReturnsAsync(true);
_pageService.Setup(p => p.PushAsync(It.IsAny<ExerciseNotes>()));
// Act
_exerciseRatingViewModel.SubmitRatingsCommand.Execute(null);
//Assert
_apiService.Verify(a => a.PostStats(It.IsAny<SequenceRating>()));
_pageService.Verify(p => p.PushAsync(It.IsAny<ExerciseNotes>()));
}
}
首先重构视图模型以避免
异步void
函数,实际事件处理程序除外
public ExerciseRatingViewModel(int sequenceID, IPageService pageService, IDataService dataService, IAPIService apiService) {
_sequenceID = sequenceID;
_pageService = pageService;
_dataService = dataService;
_apiService = apiService;
submitted += onSubmitted; //subscribe to event
SubmitRatingsCommand = new Command(() => submitted(null, EventArgs.Empty));
}
private event EventHandler submitted = delegate { };
private async void onSubmitted(object sender, EventArgs args) { //event handler
await SubmitStats();
}
private async Task SubmitStats() {
int userID = _dataService.GetUserID();
SequenceRating sequenceRating = new SequenceRating(_sequenceID, userID, DifficultyRating, PainRating, MobilityRating);
bool success = await _apiService.PostStats(sequenceRating);
await _pageService.PushAsync(new ExerciseNotes());
}
参考文献
您使用的是
任务
,因此测试需要是异步的,模拟也需要返回任务
,以允许异步按预期进行。您已经为PostStats
完成了此操作。现在只需对PushAsync
执行相同的操作
[Test()]
public async Task SubmitStats_WhenTouched_ShouldNavigateToNotesPage() {
//Arrange
var tcs = new TaskCompletionSource<object>();
_apiService.Setup(a => a.PostStats(It.IsAny<SequenceRating>())).ReturnsAsync(true);
_pageService.Setup(p => p.PushAsync(It.IsAny<Page>()))
.Callback((Page arg) => tcs.SetResult(null))
.Returns(Task.FromResult((object)null));
// Act
_exerciseRatingViewModel.SubmitRatingsCommand.Execute(null);
await tcs.Task; //wait for async flow to complete
//Assert
_apiService.Verify(a => a.PostStats(It.IsAny<SequenceRating>()));
_pageService.Verify(p => p.PushAsync(It.IsAny<ExerciseNotes>()));
}
[Test()]
公共异步任务提交状态\u何时接触\u应导航到notespage(){
//安排
var tcs=new TaskCompletionSource();
_apiService.Setup(a=>a.PostStats(It.IsAny()).ReturnsAsync(true);
_pageService.Setup(p=>p.PushAsync(It.IsAny()))
.Callback((页面参数)=>tcs.SetResult(null))
.Returns(Task.FromResult((object)null));
//表演
_exerciseRatingViewModel.SubmittingsCommand.Execute(空);
wait tcs.Task;//等待异步流完成
//断言
_验证(a=>a.PostStats(It.IsAny());
_验证(p=>p.PushAsync(It.IsAny());
}
在测试中,在模拟的回调中使用了
TaskCompletionSource
,以便在尝试验证行为之前等待异步代码完成。这是因为事件和命令在不同的线程上执行 您使用的是任务
,因此测试需要是异步的,模拟也需要返回任务
,以允许异步按预期进行。您已经为PostStats
完成了此操作。现在只需对PushAsync
执行相同的操作,避免async void
事件处理程序除外。这会让事情变得更棘手谢谢您的回答,我已经按照您的建议重构了代码。但它似乎不太正常,因为测试永远不会结束。@HenryJ好的,让我重新检查代码。我还建议在事件处理程序中放置一些断点,以确保它们被调用。我应该提到,应用程序与您提供的代码配合良好,只是测试永远不会完成谢谢您对代码的更新,但测试仍然没有完成。我必须将回调类型从ExerciseNotes对象更改为页面,因为这是服务作为参数的基类。我试过几种不同的方法,但似乎都不明白working@HenryJ我刚刚花了一些时间重新创建了到目前为止您所展示的所有代码,并在一个单元测试中运行了它,当使用我提供的答案中的细节(包括对页面的更改)进行练习时,该测试按预期通过
Task PushAsync(Page page);
public ExerciseRatingViewModel(int sequenceID, IPageService pageService, IDataService dataService, IAPIService apiService) {
_sequenceID = sequenceID;
_pageService = pageService;
_dataService = dataService;
_apiService = apiService;
submitted += onSubmitted; //subscribe to event
SubmitRatingsCommand = new Command(() => submitted(null, EventArgs.Empty));
}
private event EventHandler submitted = delegate { };
private async void onSubmitted(object sender, EventArgs args) { //event handler
await SubmitStats();
}
private async Task SubmitStats() {
int userID = _dataService.GetUserID();
SequenceRating sequenceRating = new SequenceRating(_sequenceID, userID, DifficultyRating, PainRating, MobilityRating);
bool success = await _apiService.PostStats(sequenceRating);
await _pageService.PushAsync(new ExerciseNotes());
}
[Test()]
public async Task SubmitStats_WhenTouched_ShouldNavigateToNotesPage() {
//Arrange
var tcs = new TaskCompletionSource<object>();
_apiService.Setup(a => a.PostStats(It.IsAny<SequenceRating>())).ReturnsAsync(true);
_pageService.Setup(p => p.PushAsync(It.IsAny<Page>()))
.Callback((Page arg) => tcs.SetResult(null))
.Returns(Task.FromResult((object)null));
// Act
_exerciseRatingViewModel.SubmitRatingsCommand.Execute(null);
await tcs.Task; //wait for async flow to complete
//Assert
_apiService.Verify(a => a.PostStats(It.IsAny<SequenceRating>()));
_pageService.Verify(p => p.PushAsync(It.IsAny<ExerciseNotes>()));
}