Unit testing the Viewmodel

前端 未结 6 2082
既然无缘
既然无缘 2021-02-05 18:03

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

相关标签:
6条回答
  • 2021-02-05 18:35

    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

    0 讨论(0)
  • 2021-02-05 18:35

    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.

    0 讨论(0)
  • 2021-02-05 18:36

    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
        }
    }
    
    0 讨论(0)
  • 2021-02-05 18:39

    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);
        }
    
    0 讨论(0)
  • 2021-02-05 18:42

    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"));
    }
    
    0 讨论(0)
  • 2021-02-05 18:54

    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); 
    }
    
    0 讨论(0)
提交回复
热议问题