调用模拟对象的异步方法时出现NullReferenceException

cze*_*h_u 11 c# unit-testing mstest asynchronous mocking

我正在尝试使用MOQ对以下ViewModel的LoginExecute方法进行单元测试

public class LoginViewModel : ViewModelBase, ILoginViewModel
{
    INavigationService navigationService;
    IDialogService dialogService;
    IAdminService adminService;

    public RelayCommand LoginCommand { get; set; }

    private string _productID;

    public string ProductID
    {
        get { return _productID; }
        set
        {
            _productID = value;
            RaisePropertyChanged("ProductID");
        }
    }

    public LoginViewModel(INavigationService navigationService, IDialogService dialogService, IAdminService adminService)
    {
        this.navigationService = navigationService;
        this.dialogService = dialogService;
        this.adminService = adminService;
        InitializeCommands();
    }

    private void InitializeCommands()
    {
        LoginCommand = new RelayCommand(() => LoginExecute());
    }

    public async Task LoginExecute()
    {
        await this.navigationService.TestMethod();
        this.navigationService.Navigate(typeof(ITherapistsViewModel));
    }

    public void Initialize(object parameter)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

INavigationService看起来像这样

    public interface INavigationService
{
    Frame Frame { get; set; }
    void Navigate(Type type);
    void Navigate(Type type, object parameter);
    Task TestMethod();
    void GoBack();
}
Run Code Online (Sandbox Code Playgroud)

我的测试看起来像这样

[TestMethod()]
    public async Task LoginCommandTest()
    {
        var navigationService = new Mock<INavigationService>();
        var dialogService = new Mock<IDialogService>();
        var adminService = new Mock<IAdminService>();
        LoginViewModel loginVM = new LoginViewModel(navigationService.Object, dialogService.Object, adminService.Object);

        await loginVM.LoginExecute();

        //Asserts will be here
    }
Run Code Online (Sandbox Code Playgroud)

问题在于线路时

await this.navigationService.TestMethod();
Run Code Online (Sandbox Code Playgroud)

被称为抛出NullReferenceException.如果在没有"await"的情况下调用相同的方法,它将按预期工作.如果在普通的NavigationService实现(不是模拟它)上调用该方法,它也可以正常工作.你能帮我理解为什么异步方法调用会产生NullReferenceException吗?

Ste*_*ary 17

基于任务的异步模式的一部分是隐含的假设,即该方法永远不会返回null.

这意味着对于所有异步方法,您需要模拟实际的返回值.我推荐Task.FromResult用于"同步成功"的情况,TaskCompletionSource对于"同步异常"情况,以及用于"异步成功/异常"情况的asynclambda Task.Delay.


gar*_*ryp 14

我有类似的东西; 我在异步任务上进行了回调设置,代码如下:

var navigationService = new Mock<INavigationService>()
    .Setup(s => s.TestMethod())
    .Callback(...);
Run Code Online (Sandbox Code Playgroud)

它抛出了一个模糊的NullReferenceException; 添加Returns(Task.CompletedTask)修复问题:

var navigationService = new Mock<INavigationService>()
   .Setup(s => s.TestMethod())
   .Returns(Task.CompletedTask)
   .Callback(...);
Run Code Online (Sandbox Code Playgroud)

今天被这个抓住了,希望能帮助有类似问题的人.

  • 今天早上这个确切的问题让我把头发拉了几个小时. (3认同)