单元测试Viewmodel

时间:2011-01-30 20:32:16

标签: 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) =>
                                     {
                                         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属性。对我来说,如何改进我的过程?

6 个答案:

答案 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甚至会触发的测试。

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

Here's an example of how to do that in Moq

答案 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);
}

这也将测试变成了真正的单元测试。