unit testingViewmodel

我是TDD的新手。 我已经开始在视图模型上创建我需要的属性作为普通的自动属性。

public string Firstname { get; set; } 

然后我创建了一个测试

 [TestMethod] [Tag("Property")] public void FirstNameTest() { ViewModel = new CustomerViewModel(); ViewModel.PropertyChanged += (s, e) => { Assert.AreEqual("Firstname", e.PropertyName); Assert.AreEqual("Test", ViewModel.Firstname); }; ViewModel.Firstname = "Test"; } 

然后我会扩展实际的实现,使测试通过这样:

 public string Firstname { get { return _contact.FirstName; } set { if (_contact.FirstName == value) return; _contact.FirstName = value; RaisePropertyChanged(() => Firstname); } } 

我遇到的问题是测试仍然传递给Aut属性。 任何提示,如何我可以改善我的过程?

您可以尝试将测试编写为异步。 考虑这种测试方法:

 [TestMethod] [Asynchronous] public void TestMethod1() { TestViewModel testViewModel = new TestViewModel(); bool firstNameChanged = false; testViewModel.PropertyChanged += (s, e) => { if (e.PropertyName == "FirstName") { firstNameChanged = true; } }; EnqueueCallback(() => testViewModel.FirstName = "first name"); EnqueueConditional(() => firstNameChanged == true); EnqueueTestComplete(); } 

请注意方法顶部的Asynchronous属性。 这里有两个重要的方法:EnqueueCallback和EnqueueTestComplete。 EnqueueCallback会将lambda表达式添加到队列中,测试方法将等待直到执行当前回调。 在这种情况下,我们在ViewModel上订阅PropertyChanged事件,当FirstName属性通知更改时,我们将本地布尔变量设置为true。 然后我们排队两个回调:一个用于设置FirstName属性,另一个用于断言本地布尔变量已更改值。 最后,我们需要添加对EnqueueTestComplete()的调用,以便框架知道测试结束。

注意:为了获得EnqueueCallback和EnqueueTestComplete,您需要在测试类上inheritanceSilverlightTest。 您还需要导入Microsoft.Silverlight.Testing以获取Asynchronous属性。 它应该看起来像这样:

 using Microsoft.Silverlight.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Foo.Example.Test { [TestClass] public class Tests : SilverlightTest { // ... tests go here } } 

你可以这样做:

  [TestMethod] [Tag("Property")] public void FirstNameTest() { bool didFire = false; ViewModel = new CustomerViewModel(); ViewModel.PropertyChanged += (s, e) => { didFire = true; Assert.AreEqual("Firstname", e.PropertyName); Assert.AreEqual("Test", ViewModel.Firstname); }; ViewModel.Firstname = "Test"; Assert.IsTrue(didFire); } 

您需要进行另一个实际断言您的PropertyChanged甚至会触发的测试。

当您进行该测试时,您的自动属性应该失败,因为该事件永远不会触发。

以下是如何在Moq中执行此操作的示例

测试应该失败,除非它正在测试的行为已经实现。

为了在我上次尝试中测试属性更改通知,我创建了一个帮助程序类 ,帮助我编写这样的测试(它在NUnit中)

 [Test] public void NotifiesChangeIn_TogglePauseTooltip() { var listener = new PropertyChangeListener(_mainViewModel); _mainViewModel.TogglePauseCommand.Execute(null); Assert.That(listener.HasReceivedChangeNotificationFor("TogglePauseTooltip")); } 

这是我过去做过的事情(我使用的是NUnit所以它可能有点不同):

 [Test] public void ShouldNotifyListenersWhenFirstNameChanges() { var propertiesChanged = new List(); ViewModel = new CustomerViewModel(); ViewModel.PropertyChanged += (s, e) => propertiesChanged.Add(e.PropertyName); ViewModel.Firstname = "Test"; Assert.Contains("Firstname", propertiesChanged); Assert.AreEqual("Test", ViewModel.Firstname); } 

如果它不是Firstname ,那么能够调试和解决改变的Firstname有很好的副作用。 当您从其他字段计算多个字段时非常方便。 您还可以在代码中查看行为的其他方面:

 [Test] public void ShouldNotNotifyListenersWhenPropertiesAreNotChanged() { var propertiesChanged = new List(); ViewModel = new CustomerViewModel(); ViewModel.Firstname = "Test"; ViewModel.PropertyChanged += (s, e) => propertiesChanged.Add(e.PropertyName); ViewModel.Firstname = "Test"; Assert.AreEqual(0, propertiesChanged.Count); } 

也许这个代码有更多的背景未被揭示,但我所看到的似乎不必要地复杂化。 为什么还要加入RaisePropertyChanged事件呢? 设置后只需检查属性。

 [TestMethod] [Tag("Property")] public void FirstNameTest() { var expected = "John"; var sut = new CustomerViewModel(); sut.Firstname = expected; Assert.AreEqual(expected, sut.Firstname); } 

这也将测试变成了真正的unit testing。