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:
10:
11: A.CallTo(() => enumerator.MoveNext())
12: .Returns(false)
13: .Once();
14:
15:
16:
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: }