Подтвердить что ты не робот

Тестирование модуля Viewmodel

Я как бы новичок в TDD. Я начал создавать свойства, которые мне нужны в модели представления, как обычное свойство auto.

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. Любой совет для меня, как я мог улучшить свой процесс?

4b9b3361

Ответ 1

Вы можете попробовать написать тест как асинхронный. Рассмотрим этот метод тестирования:

[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 добавит лямбда-выражения в очередь, и метод проверки будет ждать, пока не будет выполнен текущий обратный вызов. В данном случае мы подписываемся на событие PropertyChanged в ViewModel, и мы устанавливаем локальную логическую переменную в true, когда свойство FirstName уведомляет об изменении. Затем мы вызываем два обратных вызова: один, чтобы установить свойство 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

Вы можете сделать что-то вроде этого:

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

Ответ 3

У вас должен быть еще один тест, который фактически утверждает, что ваш PropertyChanged даже срабатывает.

Когда вы выполните этот тест, ваше свойство auto должно потерпеть неудачу, потому что событие никогда не срабатывает.

Вот пример того, как это сделать в Moq

Ответ 4

Тест должен завершиться неудачно, если только поведение, которое оно тестирует, уже реализовано.

Для тестирования уведомлений об изменении свойств в моей последней попытке я создал вспомогательный класс, который помог мне написать тесты вроде этого (это в NUnit)

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

   _mainViewModel.TogglePauseCommand.Execute(null);

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

Ответ 5

Вот как я это делал в прошлом (я использовал 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); 
}

Ответ 6

Возможно, есть больше фона для этого кода, который не раскрывается, но то, что я вижу, кажется излишне сложным. Зачем вообще подключаться к событию RaisePropertyChanged? Просто проверьте свойство после его настройки.

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

    sut.Firstname = expected;

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

Это также превращает тест в истинный unit test.