如何使用NUnit测试MvvmCross MvxCommand <int>

时间:2015-06-08 10:49:19

标签: parameters nunit mvvmcross execute icommand

当我的命令需要整数类型的CommandParameter时,如何为MvvmCross MvxCommand编写NUnit测试。

testListViewModel.EditCommand.Execute(null);

这个不是一个选项,因为我有这个ViewModel。如果传递参数,则执行CanExecute。

public class TestListViewModel : MvxViewModel
{
    private readonly ITestService _testService;
    private readonly IDialogService _dialogService;
    public TestListViewModel(ITestService testService, IDialogService dialogService)
    {
        _testService = testService;
        _dialogService = dialogService;
    }   

    public MvxCommand<int> EditCommand { get { return new MvxCommand<int>(Edit, id => id > 0); } }      

    private void Edit(int asTestID) { ShowViewModel<TestViewModel>(new { asTestID }); }
}

我在我的测试项目中使用NUnit + Moq + Cirrious.MvvmCross.Test.Core引用并具有此结构。

public class MockDispatcher : MvxMainThreadDispatcher, IMvxViewDispatcher
{
    public readonly List<MvxViewModelRequest> Requests = new List<MvxViewModelRequest>();
    public readonly List<MvxPresentationHint> Hints = new List<MvxPresentationHint>();
    public bool RequestMainThreadAction(Action action)
    {
        action();
        return true;
    }

    public bool ShowViewModel(MvxViewModelRequest request)
    {
        Requests.Add(request);
        return true;
    }

    public bool ChangePresentation(MvxPresentationHint hint)
    {
        Hints.Add(hint);
        return true;
    }
}

[TestFixture]
class TestListViewModelTest : MvxIoCSupportingTest
{
    [Test]
    public void TestListViewModel_OnEdit_ShowTestViewModel()
    {
        //Arrange
        base.ClearAll();

        var mockDispatcher = new MockDispatcher();
        Ioc.RegisterSingleton<IMvxViewDispatcher>(mockDispatcher);
        Ioc.RegisterSingleton<IMvxMainThreadDispatcher>(mockDispatcher);

        var mockDialogService = new Mock<IDialogService>();
        var mockTestService = new Mock<ITestService>();

        var testListViewModel = new TestListViewModel(mockTestService.Object, mockDialogService.Object);

        //Act
        testListViewModel.EditCommand.Execute(2); //this line is failing

        //Assert
        Assert.AreEqual(1, mockDispatcher.Requests.Count);
        var request = mockDispatcher.Requests[0];
        Assert.AreEqual(typeof(TestViewModel), request.ViewModelType);
        Assert.AreEqual((2).ToString(), request.ParameterValues["asTestID"]);
    }
}

当我运行测试时,它会使用此调用堆栈抛出MvxIoCResolveException。我尝试调试它,我的代码在MvxNavigatingObject.ShowViewModel()上失败,其中参数是一个对象并尝试调用 parameterValuesObject.ToSimplePropertyDictionary()
在静态函数中 ToSimplePropertyDictionary(此对象输入) propertyInfos 枚举数为null,因此无法调用 foreach(propertyInfos中的var propertyInfo)

我从运行测试中获得的调用堆栈是:

Cirrious.CrossCore.Exceptions.MvxIoCResolveException : Failed to resolve type Cirrious.MvvmCross.Platform.IMvxStringToTypeParser
   at Cirrious.CrossCore.IoC.MvxSimpleIoCContainer.Resolve(Type t)
   at Cirrious.CrossCore.IoC.MvxSimpleIoCContainer.Resolve()
   at Cirrious.MvvmCross.MvxSingletonCache.get_Parser()
   at Cirrious.MvvmCross.Platform.MvxSimplePropertyDictionaryExtensionMethods.<ToSimplePropertyDictionary>b__7(PropertyInfo property)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Cirrious.MvvmCross.Platform.MvxSimplePropertyDictionaryExtensionMethods.ToSimplePropertyDictionary(Object input)
   at Cirrious.MvvmCross.ViewModels.MvxNavigatingObject.ShowViewModel(Object parameterValuesObject, IMvxBundle presentationBundle, MvxRequestedBy requestedBy)
   at App.Core.ViewModels.TestListViewModel.Edit(Int32 asTestID) in TestListViewModel.cs: line 37
   at App.Tests.Test.TestListViewModelTest.TestListViewModel_OnEdit_ShowTestViewModel() in TestListViewModelTest.cs: line 97

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:4)

   // for navigation parsing
   Ioc.RegisterSingleton<IMvxStringToTypeParser>(new MvxStringToTypeParser());

https://github.com/MvvmCross/MvvmCross/wiki/Testing

答案 1 :(得分:0)

使用参数调用 ShowViewModel 时,有几种不同的方法可以执行此操作。这决定了MvvmCross如何调用 Init 方法。

  • 个人简单类型参数
  • 具有简单类型属性的单个Typed参数对象
  • 作为具有IMvxBundle参数的InitFromBundle() - 始终通过IMvxViewModel接口支持最后一种风格。

您使用匿名类型调用 ShowViewModel ,但没有属性名称。这些属性名称必须与 Init 方法中的参数名称匹配。

所以不要这样:

private void Edit(int asTestID) { ShowViewModel<TestViewModel>(new { asTestID }); }

这样做:

// simply-Typed parameters
private void Edit(int asTestID) { ShowViewModel<TestViewModel>(asTestID); }

或者这个:

//  a single Typed parameter object with simply-Typed properties   
private void Edit(int asTestID) { ShowViewModel<TestViewModel>(new { ID = asTestID }); }

请参阅https://github.com/MvvmCross/MvvmCross/wiki/View-Model-Lifecycle#2-init