如何为包含异步服务调用的 RelayCommand 编写单元测试?

How do I write a Unit test for a RelayCommand that contains an Async Service Call?

我有一个 RelayCommand 正在尝试测试。 RelayCommand 包含一个 Service Call 来验证我的用户。如下所示:

private MvxCommand _signIn;
public MvxCommand SignIn
{
    get
    {
        return _signIn ?? (_signIn = new MvxCommand(() =>
        {
            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
            EndpointAddress endpoint = new EndpointAddress(AppResources.AuthService);
            var client = new MyService(binding, endpoint);
            client.AuthorizeCompleted += ((sender, args) =>
            {
                try
                {
                    if (args.Result)
                    {
                        //Success.. Carry on
                    }
                }
                catch (Exception ex)
                {
                    //AccessDenied Exception thrown by service
                    if (ex.InnerException != null && string.Equals(ex.InnerException.Message, "Access is denied.", StringComparison.CurrentCultureIgnoreCase))
                    {
                        //Display Message.. Incorrect Credentials
                    }
                    else
                    {
                        //Other error... Service down?
                    }
                }
            });

            client.AuthorizeAsync(UserName, Password, null);

        }));
    }
}

但现在我正在使用 NUnit 来测试我的 ViewModels,但我不知道如何测试我的 RelayCommand

我能做到:

[Test]
public void PerformTest()
{
    ViewModel.SignIn.Execute();
}

但是这个returns没有关于SignIn方法是否成功的信息。

如何测试包含 Service CallRelayCommand

所以最后我使用 Dependency Injection 将服务注入到我的视图模型的构造函数中,如下所示:

public IMyService client { get; set; }

public MyClass(IMyService myservice)
{
    client = myservice
}

然后我可以像这样重构我的 SignIn 方法:

private MvxCommand _signIn;
public MvxCommand SignIn
{
    get
    {
        return _signIn ?? (_signIn = new MvxCommand(() =>
        {
            client.AuthorizeCompleted += ((sender, args) =>
            {
                try
                {
                    if (args.Result)
                    {
                        //Success.. Carry on
                    }
                }
                catch (Exception ex)
                {
                    //AccessDenied Exception thrown by service
                    if (ex.InnerException != null && string.Equals(ex.InnerException.Message, "Access is denied.", StringComparison.CurrentCultureIgnoreCase))
                    {
                        //Display Message.. Incorrect Credentials
                    }
                    else
                    {
                        //Other error... Service down?
                    }
                }
            });
            client.AuthorizeAsync(UserName, Password, null); 
        }));
    }
}

并使用 Assign-Act-Assert 设计模式使用模拟服务测试我的 ViewModel

    [Test]
    public void PerformTest()
    {
        List<object> objs = new List<object>();
        Exception ex = new Exception("Access is denied.");
        objs.Add(true);

        AuthorizeCompletedEventArgs incorrectPasswordArgs = new AuthorizeCompletedEventArgs(null, ex, false, null);
        AuthorizeCompletedEventArgs correctPasswordArgs = new AuthorizeCompletedEventArgs(objs.ToArray(), null, false, null);

        Moq.Mock<IMyService> client = new Mock<IMyService>();

        client .Setup(t => t.AuthorizeAsync(It.Is<string>((s) => s == "correct"), It.Is<string>((s) => s == "correct"))).Callback(() =>
        {
            client.Raise(t => t.AuthorizeCompleted += null, correctPasswordArgs);
        });


        client.Setup(t => t.AuthorizeAsync(It.IsAny<string>(), It.IsAny<string>())).Callback(() =>
        {
            client.Raise(t => t.AuthorizeCompleted += null, incorrectPasswordArgs);
        });

        var ViewModel = new MyClass(client.Object);

        ViewModel.UserName = "correct";
        ViewModel.Password = "correct";
        ViewModel.SignIn.Execute();
    }