Houman
Houman

Reputation: 66390

Unit testing the Viewmodel

I am sort of new to TDD. I have started creating the properties I need on the view model as plain auto property.

public string Firstname { get; set; }

Then I create a test

[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";
}

Then I would extend the actual implementation to make the test pass like this:

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

        _contact.FirstName = value;

        RaisePropertyChanged(() => Firstname);
    }
}

The problem I have is that test still passes for the Aut property. Any tip for me how i could improve my process?

Upvotes: 12

Views: 20473

Answers (6)

Ryan V
Ryan V

Reputation: 516

Perhaps there is more background to this code that is not being revealed, but what I am seeing seems unnecessarily complicated. Why bother hooking into the RaisePropertyChanged event at all? Just check the property after setting it.

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

    sut.Firstname = expected;

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

This also turns the test into a true unit test.

Upvotes: -1

avanek
avanek

Reputation: 1659

You could try writing the test to be asynchronous. Consider this test method:

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

Notice the Asynchronous attribute at the top of the method. There are two important methods here: EnqueueCallback and EnqueueTestComplete. EnqueueCallback will add lambda expressions to a queue and the test method will wait until the current callback is executed. In the case here, we subscribe to the PropertyChanged event on the ViewModel and we set a local boolean variable to true when the FirstName property notifies a change. We then Enqueue two callbacks: one to set the FirstName property and one to assert that the local boolean variable has changed value. Finally, we need to add a call to EnqueueTestComplete() so that the framework knows the test is over.

NOTE: In order to get EnqueueCallback and EnqueueTestComplete, you need to inherit from SilverlightTest on your test class. You also need to import Microsoft.Silverlight.Testing to get the Asynchronous attribute. It should look something like this:

using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Foo.Example.Test
{
    [TestClass]
    public class Tests : SilverlightTest
    {

        // ... tests go here
    }
}

Upvotes: 8

Lunivore
Lunivore

Reputation: 17677

Here's how I've done this in the past (I've used NUnit so it might be a bit different):

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

Has the nice side-effect of being able to debug and work out what changed, if it wasn't the Firstname. Really handy when you've got multiple fields being calculated from other fields. You can also look at the other aspect of behaviour in your code:

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

Upvotes: 1

Gishu
Gishu

Reputation: 136663

A test should fail unless the behavior it is testing is already implemented.

For testing property change notifications in my last attempt, I created a helper class that helped me write tests like this (It's in NUnit)

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

   _mainViewModel.TogglePauseCommand.Execute(null);

   Assert.That(listener.HasReceivedChangeNotificationFor("TogglePauseTooltip"));
}

Upvotes: 2

BFree
BFree

Reputation: 103770

You can do something like this:

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

Upvotes: 11

Joseph
Joseph

Reputation: 25523

You need to have another test that actually asserts that your PropertyChanged even fires.

When you make that test, then your auto property should fail, because the event will never fire.

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

Upvotes: 2

Related Questions