2011-01-30 14 views
12

Jestem trochę nowa w TDD. Zacząłem tworzyć właściwości, których potrzebuję w modelu widoku, jako zwykłą właściwość auto.Jednostka testująca Viewmodel

public string Firstname { get; set; } 

Następnie tworzę 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"; 
} 

Następnie chciałbym przedłużyć faktyczną realizację zrobić testy przechodzą tak:

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

     _contact.FirstName = value; 

     RaisePropertyChanged(() => Firstname); 
    } 
} 

Mam problem jest, że próba nadal przechodzi dla właściwości Aut. Jakaś wskazówka dla mnie, jak mogę poprawić mój proces?

+3

Nie powinieneś umieszczać Asserts w obrębie lambda. Asserts zgłasza wyjątki, gdy zawodzą. Jeśli zrobisz to w lambdach, to te będą strzelały wewnątrz obiektu-under-test i ryzykujesz, że są one obsługiwane przez obiekt. Powinieneś raczej przypisać wyniki do niektórych (zazwyczaj bool) zmiennych z zakresu testowego, a następnie powrócić do nich, gdy zwrócisz i rozwiniesz stos wywoławczy. – Tormod

Odpowiedz

5

można spróbować pisania testu być asynchroniczny. Rozważ tę metodę testu:

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

Zauważ asynchroniczny atrybut u góry metody. Istnieją dwie ważne metody: EnqueueCallback i EnqueueTestComplete. EnqueueCallback doda wyrażenia lambda do kolejki, a metoda testowa będzie czekać, aż bieżące wywołanie zwrotne zostanie wykonane. W tym przypadku subskrybujemy zdarzenie PropertyChanged w narzędziu ViewModel i ustawiamy lokalną zmienną binarną na wartość true, gdy właściwość FirstName powiadamia o zmianie. Następnie dodajemy dwa wywołania zwrotne: jeden do ustawienia właściwości FirstName i jeden do potwierdzenia, że ​​lokalna zmienna binarna zmieniła wartość. Na koniec musimy dodać wywołanie metody EnqueueTestComplete(), aby framework wiedział, że test się skończył.

UWAGA: Aby uzyskać EnqueueCallback i EnqueueTestComplete, musisz dziedziczyć z SilverlightTest na swojej klasie testowej. Musisz również zaimportować Microsoft.Silverlight.Testing, aby uzyskać atrybut asynchroniczny. Powinno to wyglądać mniej więcej tak:

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

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

     // ... tests go here 
    } 
} 
+1

Dziękuję bardzo. Ta odpowiedź pomogła mi na poziomie Silverlight jeszcze bardziej. Chciałbym jednak skomentować jedną rzecz: jeśli użyjesz EnqueueCallback bez żadnego przypadku EnqueueConditional, będzie to tak samo, jak gdybyś tak czynił synchroniczne połączenia. – Houman

+0

Tak, należy użyć EnqueueConditional, aby test naprawdę poczekał na wydarzenie. Sądzę, że mój przykład był trochę zbyt banalny, moje przeprosiny. Teraz używa EnqueueConditional, aby czekać na stan boolean firstNamedChanged, aby włączyć true. – avanek

2

Musisz mieć inny test, który faktycznie zapewnia, że ​​Twój PropertyChanged nawet strzela.

Po wykonaniu tego testu własność automatyczna powinna się nie udać, ponieważ zdarzenie nigdy się nie uruchomi.

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

+0

Wielkie dzięki za artykuł, niebawem go przestanę i zobaczę, czego jeszcze mogę się od niego nauczyć. – Houman

10

można zrobić coś takiego:

[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

Wielkie dzięki za jasną odpowiedź. Teraz działa. Jednak Aser rzuca wyjątek AssertFailedException w ostatniej linii. Muszę do F5, aby kontynuować, a potem widzę, że test zawiodł w wynikach. Używam zestawu narzędzi do testowania Silverlight 4 Unit, który jest dostarczany z VS 2010. Jest to dość denerwujące, czy można to tłumić, nie chcę F5 w późniejszych 50 testach jednostkowych, jeśli coś poszło nie tak. :) – Houman

+1

Po prostu nie uruchamiaj testów w trybie debugowania :) –

+0

Uruchomiłem go w wersji Release i nadal pojawia się przerwa. To jest bardzo irytujące. Używam testów jednostkowych w ramach Silverlight 4 Test Framework. Czy ktokolwiek ma pomysł? – Houman

2

test nie powiedzie się, chyba że zachowanie to jest testowanie jest już realizowany.

Do testowania powiadomienia o zmianach nieruchomość w mojej ostatniej próbie, stworzyłem a helper class że pomógł mi napisać testy tak (To w NUnit)

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

    _mainViewModel.TogglePauseCommand.Execute(null); 

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

Oto jak robiłem to w przeszłości (Użyłem NUnit więc może być nieco inny):

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

Posiada piękny bok -możliwość debugowania i sprawdzenia, co się zmieniło, jeśli nie było to Firstname. Naprawdę przydatne, gdy oblicza się wiele pól z innych pól.Można również spojrzeć na inne aspekty zachowań w kodzie:

[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

Może jest bardziej tło do tego kodu, który nie jest objawione, ale to, co widzę wydaje się niepotrzebnie skomplikowane. Po co zawracać sobie głowę w ogóle wydarzeniem RaisePropertyChanged? Po prostu sprawdź właściwość po jej ustawieniu.

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

    sut.Firstname = expected; 

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

To również zmienia test w prawdziwy test jednostkowy.

+0

To byłoby trochę bezużyteczne. Po co zasób, który platforma jest w stanie poprawnie przetworzyć ustawiającą autoprawertę? Test zaczyna mieć sens w momencie, w którym chcesz sprawdzić, czy zdarzenia "PropertyChanged" zostały podniesione zgodnie z oczekiwaniami. ** To ** jest jedyną rzeczą wartą sprawdzenia w takim teście. – Martin

+0

@Martin Zgadzam się, że testowanie ustawiciela autoproperty nie przyniesie żadnych korzyści. Jeśli jednak spojrzysz na oryginalny kod (opublikowany przez Hooman), zauważysz, że właściwość jest ustawiona tylko wtedy, gdy wartość przychodząca nie jest równa wartości _contact.FirstName. Jest to część kodu, której dotyczy mój test refaktoryzacji. – hitopp

Powiązane problemy