Testowanie setterów w fakeiteasy

2

Na dzień dzisiejszy wybierając "mocking framework" stawiam właśnie na fakeiteasy. Ma ona jeden ciemny zakamar, w którym można nieźle pobłądzić… a jest to testowanie wywołania settera.

Załóżmy, że mamy jakiś interfejs wymuszający na implementacjach posiadanie właściwości Age:

  1:  public interface IHaveAge
  2:  {
  3:      int Age { get; set; }
  4:  }

I test "jakiejś klasy" przetwarzającej implementacje tego interfejsu, mającej ustawić wartość wspomnianej właściwości:

  1:  [Fact]
  2:  public void sets_age_to_18_during_processing()
  3:  {
  4:      var withAge = A.Fake<IHaveAge>();
  5:  
  6:      new TestedClass().Process(withAge);
  7:  
  8:      A.CallTo(() => withAge.Age = 18)
  9:          .MustHaveHappened();
 10:  }

Wydaje się w porządku? Otóż nie jest. Kompilator zgłosi, że jesteśmy fe:

Error    5    An expression tree may not contain an assignment operator

Sam powyższy scenariusz jest jeszcze w miarę prosty, bo przypisanie konkretnej wartości do settera możemy przetestować w "normalny" sposób – fakeiteasy zapamiętuje przypisanie i konfiguruje mocka tak aby zwracał aktualnie nadaną właściwości wartość:

  1:  [Fact]
  2:  public void sets_age_during_processing()
  3:  {
  4:      var withAge = A.Fake<IHaveAge>();
  5:  
  6:      new TestedClass().Adapt(withAge);
  7:  
  8:      Assert.Equal(18, withAge.Age);
  9:  }

 

Ale problem pojawia się, jeśli chcemy przetestować sam fakt wywołania settera, a nie jego aktualną wartość. Sytuacja taka może być na nas wprost wymuszona, na przykład jeśli mockujemy interfejs z zewnętrznej biblioteki i ma on właściwość write-only, bez gettera (rzadki przypadek, ale się zdarza). Jedynym chyba sposobem na napisanie takiego testu w fakeiteasy jest poniższy brzydal:

  1:  [Fact]
  2:  public void sets_age_during_processing()
  3:  {
  4:      var withAge = A.Fake<IHaveAge>();
  5:  
  6:      new TestedClass().Adapt(withAge);
  7:  
  8:      A.CallTo(withAge)
  9:          .Where(x =>
 10:                  x.Method.Name == "set_Age"
 11:                  // if we want to test the value as well
 12:                  // && x.GetArgument<int>(0) == 18
 13:      ).MustHaveHappened();
 14:  }

Okropne, ale… nie znalazłem lepszego sposobu (nie tylko ja, wiem że i Gutek trochę nad tym posiedział…).

Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

2 Comments

  1. Co do 3. przypadku, to brzydka wydaje mi się koncepcja interfejsu z setterem typu write-only (nie udostępniającego sposobu na sprawdzenie skutków takiego wywołania). Nie dziwi mnie dlatego, że konfiguracja mocka jest w tym wypadku równie brzydka :]