C# Viewmodel的单元测试
我是TDD的新手。我已经开始在视图模型上创建所需的属性,作为“纯自动”属性C# Viewmodel的单元测试,c#,.net,silverlight,unit-testing,tdd,C#,.net,Silverlight,Unit Testing,Tdd,我是TDD的新手。我已经开始在视图模型上创建所需的属性,作为“纯自动”属性 public string Firstname { get; set; } 然后我创建一个测试 [TestMethod] [Tag("Property")] public void FirstNameTest() { ViewModel = new CustomerViewModel(); ViewModel.PropertyChanged += (s, e) =>
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);
}
}
[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);
}
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Foo.Example.Test
{
[TestClass]
public class Tests : SilverlightTest
{
// ... tests go here
}
}
我的问题是Aut属性的测试仍然通过。有什么建议可以告诉我如何改进我的流程吗?您需要进行另一项测试,以确认您的属性甚至发生了更改 当您进行该测试时,您的auto属性应该失败,因为事件永远不会触发
您可以这样做:
public string Firstname
{
get { return _contact.FirstName; }
set
{
if (_contact.FirstName == value)
return;
_contact.FirstName = value;
RaisePropertyChanged(() => Firstname);
}
}
[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);
}
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Foo.Example.Test
{
[TestClass]
public class Tests : SilverlightTest
{
// ... tests go here
}
}
除非测试的行为已经实现,否则测试应该失败 为了在我上一次尝试中测试属性更改通知,我创建了一个帮助我编写类似这样的测试(在NUnit中)
您可以尝试将测试编写为异步的。考虑这个测试方法:
[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以获取异步属性。它应该是这样的:
public string Firstname
{
get { return _contact.FirstName; }
set
{
if (_contact.FirstName == value)
return;
_contact.FirstName = value;
RaisePropertyChanged(() => Firstname);
}
}
[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);
}
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Foo.Example.Test
{
[TestClass]
public class Tests : SilverlightTest
{
// ... tests go here
}
}
以下是我过去是如何做到这一点的(我使用了NUnit,所以可能会有点不同):
也许这段代码还有更多的背景没有被揭示出来,但我看到的似乎是不必要的复杂。为什么要费心去钩住
RaisePropertyChanged
事件呢?设置属性后,只需检查属性
[TestMethod]
[Tag("Property")]
public void FirstNameTest()
{
var expected = "John";
var sut = new CustomerViewModel();
sut.Firstname = expected;
Assert.AreEqual(expected, sut.Firstname);
}
这也将把测试变成一个真正的单元测试。非常感谢您给出了清晰的答案。现在可以了。但是,断言在最后一行抛出AssertFailedException。我必须F5它继续,然后我看到测试失败的结果。我正在使用VS2010附带的Silverlight 4单元测试工具包。这很烦人,是否可以抑制这种情况,如果出现问题,我不想稍后在50个单元测试中使用F5只是不要在调试模式下运行测试:)我在发行版下运行了它,但仍然会出现中断。这很烦人。我在Silverlight4测试框架下使用单元测试。有人知道吗?我找到了。这与调试模式无关答案是:@Antoine Aubry,或者他可以点击继续按钮或按F5。这是同样的行为。非常感谢这篇文章,我会很快研究它,看看我还能从中学到什么。不幸的是,链接已经死了,答案中没有复制这篇文章的信息。非常感谢。这个答案在Silverlight级别上对我帮助更大。然而,我想评论一件事,如果您使用EnqueueCallback而不使用任何EnqueueConditional的情况,这与您将调用同步是一样的。是的,应该使用EnqueueConditional使测试真正等待事件触发。我想我的例子太琐碎了,我道歉。它现在使用EnqueueConditional等待firstNamedChanged的布尔状态变为true。您不应该在lambda中放置断言。断言失败时抛出异常。如果您在lambda中这样做,那么这些将在被测试对象中激发,并且您将面临被对象处理的风险。相反,您应该将结果分配给测试范围中的一些(通常是bool)变量,然后在返回并解开调用堆栈时针对这些变量进行断言。这将有些无用。为什么框架能够正确处理自动属性的设置器?当您希望检查
PropertyChanged
事件是否按预期引发时,测试就开始有意义了。在这样的测试中,这是唯一值得检查的东西。@Martin我同意测试自动属性的设置器不会带来任何好处。但是,如果查看原始代码(由Hooman发布),您会注意到,只有当传入值不等于_contact.FirstName中的值时,才会设置该属性。这就是我的重构测试所涉及的代码部分。