我是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属性。对我来说,如何改进我的过程?
答案 0 :(得分:10)
您可以这样做:
[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);
}
答案 1 :(得分:7)
您可以尝试将测试编写为异步。考虑这种测试方法:
[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,您需要在测试类上继承SilverlightTest。您还需要导入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
}
}
答案 2 :(得分:2)
您需要进行另一项实际断言您的PropertyChanged甚至会触发的测试。
当您进行该测试时,您的自动属性应该失败,因为事件永远不会触发。
答案 3 :(得分:2)
测试应该失败,除非它正在测试的行为已经实现。
为了在上次尝试中测试属性更改通知,我创建了a helper class,帮助我编写了这样的测试(它在NUnit中)
[Test]
public void NotifiesChangeIn_TogglePauseTooltip()
{
var listener = new PropertyChangeListener(_mainViewModel);
_mainViewModel.TogglePauseCommand.Execute(null);
Assert.That(listener.HasReceivedChangeNotificationFor("TogglePauseTooltip"));
}
答案 4 :(得分:1)
过去我是如何做到这一点的(我使用的是NUnit,所以可能会有所不同):
[Test]
public void ShouldNotifyListenersWhenFirstNameChanges()
{
var propertiesChanged = new List<string>();
ViewModel = new CustomerViewModel();
ViewModel.PropertyChanged += (s, e) => propertiesChanged.Add(e.PropertyName);
ViewModel.Firstname = "Test";
Assert.Contains("Firstname", propertiesChanged);
Assert.AreEqual("Test", ViewModel.Firstname);
}
如果它不是Firstname
,那么能够调试和计算出更改的内容具有很好的副作用。当您从其他字段计算多个字段时非常方便。您还可以在代码中查看行为的其他方面:
[Test]
public void ShouldNotNotifyListenersWhenPropertiesAreNotChanged()
{
var propertiesChanged = new List<string>();
ViewModel = new CustomerViewModel();
ViewModel.Firstname = "Test";
ViewModel.PropertyChanged += (s, e) => propertiesChanged.Add(e.PropertyName);
ViewModel.Firstname = "Test";
Assert.AreEqual(0, propertiesChanged.Count);
}
答案 5 :(得分:-1)
也许这个代码有更多的背景未被揭示,但我所看到的似乎不必要地复杂化。为什么还要加入RaisePropertyChanged
事件呢?只需在设置后检查该属性。
[TestMethod]
[Tag("Property")]
public void FirstNameTest()
{
var expected = "John";
var sut = new CustomerViewModel();
sut.Firstname = expected;
Assert.AreEqual(expected, sut.Firstname);
}
这也将测试变成了真正的单元测试。