2011-01-30 5 views
12

Я как бы новичок в TDD. Я начал создавать свойства, которые мне нужны в модели представления, как обычное свойство auto.Тестирование модуля 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

Вы не должны размещать Asserts внутри лямбда. Проверяет исключения при неудаче. Если вы сделаете это в лямбдах, тогда они будут стрелять внутри объекта, находящегося под тестом, и вы рискуете подвергнуться обработке объекта. Вместо этого вы должны назначать результаты некоторым (обычно bool) переменным из области проверки, а затем утверждать их против тех, когда вы возвращаете и разматываете стек вызовов. – Tormod

ответ

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 добавит лямбда-выражения в очередь, и метод проверки будет ждать, пока не будет выполнен текущий обратный вызов. В данном случае мы подписываемся на событие 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 
    } 
} 
+1

Большое спасибо. Этот ответ помог мне на уровне Silverlight еще больше. Однако я хотел бы прокомментировать одно: если вы используете EnqueueCallback без какого-либо случая EnqueueConditional, это будет так же, как если бы вы делали вызовы синхронными в любом случае. – Houman

+0

Да, EnqueueConditional следует использовать для того, чтобы сделать тест по-настоящему ожидающим возникновения события. Наверное, я сделал свой пример немного тривиальным, мои извинения. Теперь он использует EnqueueConditional для ожидания в булевом состоянии firstNamedChanged, чтобы перейти к true. – avanek

2

Вам нужно иметь еще одно испытание, которое фактически утверждает, что ваш PropertyChanged даже срабатывает.

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

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

+0

Большое спасибо за статью, я буду изучать ее в ближайшее время и посмотреть, что еще я могу извлечь из нее. – Houman

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, чтобы продолжить, а затем я вижу, что тест не удался в результатах. Я использую Silverlight 4 Unit testing toolkit, который поставляется с VS 2010. Это довольно раздражает, можно ли это подавить, я не хочу, чтобы F5 на 50 модульных тестах позже, если что-то пошло не так. :) – Houman

+1

Просто не запускайте тесты в режиме отладки :) –

+0

Я запускал его под Release, и он все еще появляется с прерыванием. Это очень раздражает. Я использую модульное тестирование в Silverlight 4 Test Framework. Кто-нибудь знает? – Houman

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

Это также превращает испытание в настоящий модульный тест.

+0

Это было бы несколько бесполезно. Почему актив, который структура может правильно обработать установщика автопроцесса? Тест начинает иметь смысл в тот момент, когда вы хотите проверить, что события «PropertyChanged» подняты, как ожидалось. ** Это единственное, что стоит проверить в таком тесте. – Martin

+0

@ Мартин Я согласился бы, что тестирование установщика автопробега не принесет никакой пользы. Однако, если вы посмотрите на исходный код (отправленный Hooman), вы заметите, что свойство установлено только в том случае, если входящее значение не равно значению в _contact.FirstName. Это часть кода, с которой мой рефакторинговый тест. – hitopp

Смежные вопросы