2011-01-30 39 views
12

我对TDD有点新鲜。我已经开始在视图模型上创建我需要的属性作为普通的自动属性。单元测试Viewmodel

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属性。对我来说任何提示我如何改进我的过程?

+3

你不应该把断言拉姆达内。断言在失败时会抛出异常。如果你在lambda表达式中这样做,那么这些将在被测对象内部激发,并且冒着被对象处理的风险。您应该将结果分配给测试范围中的一些(通常为bool)变量,然后在您返回并解开调用堆栈时对这些变量进行断言。 – Tormod 2011-04-25 14:49:02

回答

5

你可以尝试写测试是异步的。考虑这种测试方法:

[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(); 
} 

注意方法顶部的异步属性。这里有两个重要的方法:EnqueueCallback和EnqueueTestComplete。 EnqueueCallback会将lambda表达式添加到队列中,并且测试方法将等待直到执行当前回调。在这种情况下,我们订阅ViewModel上的PropertyChanged事件,并且当FirstName属性通知更改时我们将本地布尔变量设置为true。然后,我们Enqueue两个回调函数:一个设置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

您需要另一个测试,确实声称您的PropertyChanged甚至会触发。

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

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

+0

很多感谢这篇文章,我会尽快研究它,看看我还能从中学到什么。 – Houman 2011-01-30 22:47:07

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); 
    } 
+0

很多感谢您的明确答案。它现在有效。然而,Assert在最后一行抛出一个AssertFailedException。我必须F5继续,然后我看到测试结果失败。我使用的是随2010年这是很烦人的VS Silverlight 4的单元测试工具,是能够抑制这个问题,我不想对F5 50个单元测试以后,如果出事了。 :) – Houman 2011-01-30 22:46:35

+1

只要不运行在调试模式下测试:) – 2011-01-31 15:07:52

+0

我跑它释放下,它仍然受到干扰出现。它非常烦人。我正在使用Silverlight 4测试框架下的单元测试。任何想法的人? – Houman 2011-02-01 23:27:50

2

测试应该会失败,除非行为是测试已经实现。

这是我最后一次尝试测试属性更改通知,我创建a helper class,帮助我写这样的测试(这是在NUnit的)

[Test] 
public void NotifiesChangeIn_TogglePauseTooltip() 
{ 
    var listener = new PropertyChangeListener(_mainViewModel); 

    _mainViewModel.TogglePauseCommand.Execute(null); 

    Assert.That(listener.HasReceivedChangeNotificationFor("TogglePauseTooltip")); 
} 
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); 
} 
-1

或许有更多的背景,这个代码没有被透露,但我所看到的似乎是不必要的复杂。为什么麻烦勾起RaisePropertyChanged事件呢?设置后请检查属性。

[TestMethod] 
[Tag("Property")] 
public void FirstNameTest() 
{ 
    var expected = "John"; 
    var sut = new CustomerViewModel(); 

    sut.Firstname = expected; 

    Assert.AreEqual(expected, sut.Firstname); 
} 

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