Reputation: 7525
I have a property in my viewmodel whose sole job is to send out a message when the property is changed. I wanted to unit test that it was indeed doing it's job so I wrote the following test
[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);
}
}
This test asserts the correct behavior, but it occassionally fails both locally and on my build server due to a threading error. The error is as follows
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)
Is there a better way I can write this test that would prevent it from failing randomly in this way?
Upvotes: 2
Views: 334
Reputation: 6882
OK this is the approach I usually take. This is pretty close to what you do. Keep in Mind you can also highjack the Messenger and subscribe to the Messenger In the unit test.
I posted a simplified version of your code to match to example.
I used to have also some occansionly strange errors in my unit tests and it was a timing/thread issue...
for that it helped to reset the Messenger foreach Unit test...
This is the 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;
}
}
this is the Unit test (using MSTest but could apply to other packages)...
[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 this is simplyfied to show the concept.
I did just passed a string for message payload but this can be as complex as you want with all messenger functionalities....
I hope this helps... and otherwise it would be helpful to see the ViewModel code to cum up with another test strategy ...
P.S.: I also developed the habit to have 1 assertion per Test only and write more tests instead...
Upvotes: 1