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

15

我正在尝试使用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)
    {
    }
}

INavigationService的样子如下所示

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

我的测试看起来像这样

[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
    }

问题在于当行

await this.navigationService.TestMethod();

被称为 NullReferenceException 的异常被抛出。如果不使用 "await" 调用同样的方法,则会按预期工作。如果在正常的 NavigationService 实现上调用该方法(而不是其模拟),它也可以正常工作。请帮助我理解为什么异步方法调用会产生 NullReferenceException?

2个回答

20

任务异步模式的一部分是假设方法永远不会返回null

这意味着对于所有的异步方法,您都需要模拟一个实际的返回值。我建议在“同步成功”情况下使用Task.FromResult,在“同步异常”情况下使用TaskCompletionSource,在“异步成功/异常”情况下使用带有Task.Delayasync lambda。


19

我曾经也有类似的经历;我设置了一个异步任务回调,并写出了以下类似的代码:

var navigationService = new Mock<INavigationService>()
    .Setup(s => s.TestMethod())
    .Callback(...);

它抛出了一个不明确的 NullReferenceException;添加 Returns(Task.CompletedTask) 修复了这个问题:

var navigationService = new Mock<INavigationService>()
   .Setup(s => s.TestMethod())
   .Returns(Task.CompletedTask)
   .Callback(...);

今天我也遇到了这个问题,希望这篇文章能帮助到其他碰到相似问题的人。


3
今天早上这个问题让我抓狂了好几个小时。 - Dark Hippo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接