fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
2 minut

Mockowanie IEnumerable/IEnumerator z fakeiteasy


15.12.2011

Niedawno po raz pierwszy w życiu musiałem mockować implementację interfejsu IEnumerable<T>. Chodziło o jakieś dziwne struktury używane wewnętrznie przez FIM. Problem polegał na tym, że obiekt mockowanego przeze mnie typu zwracał kolekcję innych obiektów. Ta kolekcja była właśnie IEnumerable<X>… ale nie mogłem stworzyć jej instancji, ponieważ wspomniana klasa XCollection była abstrakcyjna, a jej implementacja siedziała zaszyta gdzieś wewnątrz jakichś dllek. Jednocześnie chciałem przetestować swój kod, podając mu tak spreparowany mock, aby dało się jeździć po XCollection pętlą foreach oraz LINQ (które to stwierdzenia znaczą właściwie to samo).

Okazało się, że trzeba było trochę pogłówkować. Poniżej kod realizujący to zadanie, od razu z testami sprawdzającymi poprawność stworzonego mocka:

  1:  private IEnumerable<T> create_fake_enumerable<T>(params T[] items)
  2:  {
  3:      var enumerable = A.Fake<IEnumerable<T>>();
  4:      var enumerator = A.Fake<IEnumerator<T>>();
  5:  
  6:      A.CallTo(() => enumerable.GetEnumerator())
  7:          .Returns(enumerator);
  8:  
  9:      // fake configuration is LIFO ->
 10:      // -> define last action first
 11:      A.CallTo(() => enumerator.MoveNext())
 12:          .Returns(false)
 13:          .Once();
 14:  
 15:      // define true for each element in enumerator
 16:      // overriding earlier configuration
 17:      A.CallTo(() => enumerator.MoveNext())
 18:          .Returns(true)
 19:          .NumberOfTimes(items.Length);
 20:  
 21:      A.CallTo(() => enumerator.Current)
 22:          .ReturnsNextFromSequence(items);
 23:  
 24:      return enumerable;
 25:  }
 26:  
 27:  [Fact]
 28:  public void is_usable_in_linq()
 29:  {
 30:      var enumerable = create_fake_enumerable(3, 6, 9);
 31:  
 32:      var between4and9 = enumerable.Where(x => x > 4 && x < 9);
 33:  
 34:      Assert.Equal(1, between4and9.Count());
 35:  }
 36:  
 37:  [Fact]
 38:  public void is_usable_in_foreach()
 39:  {
 40:      var enumerable = create_fake_enumerable(6, 6, 6, 6);
 41:  
 42:      int counter = 0;
 43:  
 44:      foreach (var number in enumerable)
 45:      {
 46:          Assert.Equal(6, number);
 47:          counter++;
 48:      }
 49:  
 50:      Assert.Equal(4, counter);
 51:  }
0 0 votes
Article Rating
7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
wiero
wiero
12 years ago

czy mógłbyś przybliżyć przewagę stowrzonego mocka nad rzutowaniem argumentu items na IEnumerable<T>?

procent
12 years ago

wiero,
Chyba nie rozumiem pytania. Rzutowaniem argumentu w metodzie create_fake_enumerable, czy w samym teście? Jeśli w samym teście to tak jak napisałem we wstępie – nie miałem takiej możliwości. Rzadka sytuacja i przydarzyła mi się pierwszy raz, ale fantacja grupy produktowej FIM jest nieograniczona:).

wiero
wiero
12 years ago

chodziło mi o metodę create_fake_enumerable, nie do końca załapałem czemu tworzony jest jest mock jeśli sama T[] może zachowywać się jak IEnumerable<T>

procent
12 years ago

wiero,
Hmm… muszę się nad tym zastanowić. To by było strasznie głupie, gdybym robił takie konstrukcje bez potrzeby:). Implementowałem to ze 2 miesiące temu, postaram się wieczorem sprawdzić dlaczego robiłem tak a nie inaczej i odpiszę.

procent
12 years ago

OK, już mam. Musiałem zamockować właściwość "attribute.Values", gdzie "Values" jest typu "ValueCollection". Ten typ implementuje IEnumeratble (niegeneryczny) i jest to klasa abstrakcyjna – nie mogę z niej bezpośrednio skorzystać. Jej implementacja jest gdzieś głęboko ukryta – nie mam do niej dostępu z poziomu testów. Jej metoda GetEnumerator zwraca ValueCollectionEnumerator – też klasa abstrakcyjna, też nie mam dostępu do implementacji.

Faktycznie może stąd pewien "mismatch" między moim przykładem a faktycznym zastosowaniem "w życiu" – w prawdziwych warunkach nie mogłem ani niczego rzutować na jawnie dostępny typ. Masz całkowitą rację – w przykładzie spokojnie można by było rzutować na IEnumerable<T> i spokój. W mojej konkretnej sytuacji nie było to IEnumerable<T>, tylko ValueCollection i ValueCollectionEnumerator…

Tomek
Tomek
12 years ago

Banalne, ale intrygujące mnie pytanie: dlaczego nazwy metod rozpoczynasz od małej litery?

procent
12 years ago

Tomek,
A jakoś tak mi się przyjęło że w testach używam "notacji_podkreśleniowej":). Post na ten m.in. temat jest w trakcie tworzenia, powinien się w tym tygodniu ukazać.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również