有没有更好的方法来测试这个不会导致偶尔出现线程错误的MVVM Light Message?

时间:2014-05-01 11:07:59

标签: wpf unit-testing mvvm mvvm-light

我的viewmodel中有一个属性,其唯一的工作是在属性更改时发送消息。我想进行单元测试,确实这样做了,所以我编写了以下测试

[TestFixture, RequiresSTA]
public class ToolBarViewModelTests
{
    private SecondaryScalingMode _SecondaryScalingMode;

    private void RecordSecondaryScalingSetting(SecondaryScalingMode obj)
    {
        _SecondaryScalingMode = obj;
    }

    [Test]
    public void AssertCombineAllCornersScalingMessageIsSent()
    {
        var vm = CreateNewToolBar();

        _SecondaryScalingMode = SecondaryScalingMode.IndividualCorner;

        AppMessages.SetSecondaryScalingMode.Register(this, (Action<SecondaryScalingMode>)RecordSecondaryScalingSetting);

        vm.CombineAllCornersScaling = true;
        Assert.IsFalse(vm.IndividualCornerScaling);
        Assert.IsFalse(vm.LeftRightScaling);
        Assert.IsFalse(vm.FrontRearScaling);
        Assert.IsTrue(vm.CombineAllCornersScaling);
        Assert.AreEqual(SecondaryScalingMode.CombineAllCorners, _SecondaryScalingMode);
    }
}

此测试断言正确的行为,但由于线程错误,它在本地和我的构建服务器上偶尔会失败。错误如下

Test(s) failed. System.Reflection.TargetInvocationException : Exception has been thrown         by the target of an invocation.  
----> System.InvalidOperationException : The calling thread cannot access this object because a different thread owns it.  
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)

我是否有更好的方法来编写此测试,以防止它以这种方式随机失败?

1 个答案:

答案 0 :(得分:1)

好的,这是我经常采取的方法。这与你的工作非常接近。 请记住,您还可以在单​​元测试中劫持Messenger并订阅Messenger。

我发布了一个简化版的代码,以便与示例相匹配。

我以前在单元测试中也有一些奇怪的错误,这是一个时间/线程问题......

因为它有助于重置Messenger foreach单元测试......

这是viewmodel(simplefied guess work =)...):

public class UnitTestViewModel:ViewModelBase
{
    public UnitTestViewModel()
    {
        Messenger.Default.Register<string>(this, "SetSecondaryScalingMode", (message) =>
        {
            UpdateScalingMode();
        });
    }

    private bool _CombineAllCornersScaling;
    public bool CombineAllCornersScaling
    {
        get {
            return _CombineAllCornersScaling;
        }
        set {
            _CombineAllCornersScaling = value;
            Messenger.Default.Send<string>("update", "SetSecondaryScalingMode");
            RaisePropertyChanged("CombineAllCornersScaling");
        }
    }

    private bool _IndividualCornerScaling;
    public bool IndividualCornerScaling
    {
        get
        {
            return _IndividualCornerScaling;
        }
        set
        {
            _IndividualCornerScaling = value;

            RaisePropertyChanged("IndividualCornerScaling");
        }
    }

    private bool _LeftRightScaling;
    public bool LeftRightScaling
    {
        get
        {
            return _LeftRightScaling;
        }
        set
        {
            _LeftRightScaling = value;

            RaisePropertyChanged("LeftRightScaling");
        }
    }

    private bool _FrontRearScaling;
    public bool FrontRearScaling
    {
        get
        {
            return _FrontRearScaling;
        }
        set
        {
            _FrontRearScaling = value;

            RaisePropertyChanged("FrontRearScaling");
        }
    }

    private void UpdateScalingMode()
    {
        IndividualCornerScaling = false;
        LeftRightScaling = false;
        FrontRearScaling = true;
    }
}

这是单元测试(使用MSTest但可以应用于其他包)...

 [TestMethod]
   public void UpdaetScalingMode()
   {
       Messenger.Reset();
       var vm = new UnitTestViewModel();
       bool updateMessageWasSend = false;
       string _thisCanBeTheObjectYouWerePassing = String.Empty;
       Messenger.Default.Register<string>(vm, "SetSecondaryScalingMode", (msg) =>
       {
           _thisCanBeTheObjectYouWerePassing = msg;
           updateMessageWasSend = true;
       });

       vm.CombineAllCornersScaling = true;

       Assert.AreEqual(true, updateMessageWasSend);
       Assert.AreEqual(false, vm.IndividualCornerScaling);
       Assert.AreEqual(false, vm.LeftRightScaling);
       Assert.AreEqual(true, vm.CombineAllCornersScaling);
       Assert.AreEqual("update", _thisCanBeTheObjectYouWerePassing);

   }

Rember只是为了展示这个概念。

我刚刚传递了一个消息有效负载的字符串,但这可能与所有信使功能一样复杂....

我希望这会有所帮助......否则看看ViewModel代码与其他测试策略相结合会很有帮助...

P.S。:我还养成了每次测试只有1个断言并且编写更多测试的习惯......