【问题标题】:How to organize code in Xamarin Forms viewmodel for unit testing?如何在 Xamarin Forms 视图模型中组织代码以进行单元测试?
【发布时间】:2016-05-19 20:18:11
【问题描述】:

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

所以典型的 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 的测试。

我不确定其他人是如何处理单元测试视图模型的,我能找到的大部分信息都有一些非常简单的示例,它们从未解决我一直遇到的问题。

【问题讨论】:

    标签: unit-testing ninject xamarin.forms mvvm-light


    【解决方案1】:

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

    【讨论】:

    • 谢谢。是的,我想到为什么不将所有静态调用包装在服务中,然后我可以模拟它们以返回我想要的内容或提供测试实现。我想我的脑袋被“它的移动”所包围,以至于无法想到我通常会在任何其他项目类型中做什么。这也是我刚开始时没有 TDD,只是开始编写代码的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-01
    • 2011-07-25
    • 2013-03-15
    • 1970-01-01
    • 2012-12-25
    相关资源
    最近更新 更多