如何在 Xamarin Forms 视图模型中组织代码以进行单元测试?

How to organize code in Xamarin Forms viewmodel for unit testing?

我有一个使用 Mvvm Light 和 Ninject 的 XF 项目。 我正在尝试回溯并为我的视图模型编写单元测试,但我将 运行 保留在需要调用 Xamarin.Forms.Init() 或我引用 Device.BeginInvokeOnMainThread 或其他 [=31= 的问题中] RelayCommands 中的特定内容。

所以典型的 RelayCommand 可能如下所示:

public RelayCommand DeleteGameCommand
{
    get
    {
        return _deleteGameCommand
            ?? (_deleteGameCommand = new RelayCommand(
            async () =>
            {
                Device.BeginInvokeOnMainThread(() => ToggleMasterMenuDisplayCommand.Execute(null));
                var response = await
                    ((MasterDetailPage) Application.Current.MainPage).Detail.DisplayActionSheet("Are you sure?", "Cancel",
                        "Delete Game");
                if (response == "Delete Game")
                {
                    StopTimer();

                    using (new Busy(this, "Deleting Game..."))
                    {
                        await GameService.DeleteGame(Game, true);
                        await AzureService.SyncGameInfoForGame(Game.GameId, true);
                    }

                    App.Current.Container.Get<DashboardViewModel>().ShouldRefreshDataOnNextDisplay();
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        App.Current.HideLoading();
                        LoadHomeAsMainPage();
                    });
                }
            }));
    }
}

Busy() 调用一些 UI 东西来显示进度指示器。 App.Current.Container 是我的 Ninject 内核。

我是否应该将其重构为更类似的东西:

public RelayCommand DeleteGameCommand
{
    get
    {
        return _deleteGameCommand
            ?? (_deleteGameCommand = new RelayCommand(
            async () =>
            {
                Device.BeginInvokeOnMainThread(() => ToggleMasterMenuDisplayCommand.Execute(null));
                var response = await
                    ((MasterDetailPage) Application.Current.MainPage).Detail.DisplayActionSheet("Are you sure?", "Cancel",
                        "Delete Game");
                if (response == "Delete Game")
                {
                    StopTimer();

                    using (new Busy(this, "Deleting Game..."))
                    {
                        await BusinessLogic.DeleteGame(Game,true);
                    }

                    App.Current.Container.Get<DashboardViewModel>().ShouldRefreshDataOnNextDisplay();
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        App.Current.HideLoading();
                        LoadHomeAsMainPage();
                    });
                }
            }));
    }
}



public class BusinessLogic
{
    public async Task DeleteGame(Game game, bool includeRelated)
    {
        await GameService.DeleteGame(game, true);
        await AzureService.SyncGameInfoForGame(game.GameId, true); 
    }
}

我意识到这可能不是最好的例子,因为这两个服务调用很可能已经过单元测试,但在其他中继命令中,除了调用服务之外还有更多的事情要做。

我在想,如果我做这样的事情,那么我就可以对所有业务逻辑进行单元测试,而永远不会编写调用 RelayCommand 的测试。

我不确定其他人是如何进行单元测试视图模型的,我能找到的大部分信息都有非常简单的示例,它们从来没有解决我 运行 关注的问题。

通常您会创建并调用包装器,这样当您进行自动化测试时,这些存根就会 运行 代替。您模拟的 Init 和 BeginInvokeOnMainThread 可能会转发到不同的实现。