Gdy tworzymy testy jednostkowe bardzo często musimy coś „zmockować” (czyli stworzyć sztuczny obiekt, którego będziemy używali w trakcie testów zamiast prawdziwej implementacji). Niestety czasem bywa tak, że nasza klasa ma dużo zależności, a do naszego testu potrzebujemy tylko niektórych z nich. Mimo to musimy stworzyć mock dla każdej z zależności, bo inaczej nie będziemy mogli wykonać testu. Czasem bywa też tak, że potrzebujemy dodać nową zależność do istniejącej klasy, a potem trzeba przerobić wszystkie testy i w każdym z nich dodać mock do nowej zależności – nawet jeśli one tej zależności nie wykorzystują.
Przyznam szczerze, że takie sytuacje powodowały, że nie chciało mi się pisać testów. Byłem im przeciwny, bo drobna zmiana powodowała godziny poświęcone na poprawianie testów. Jednak jakiś czas temu natknąłem się na bibliotekę, która automatycznie tworzy mocki dla wszystkich zależności. My potem tylko dostosowujemy te, które są niezbędne dla testów. To znacząco zredukowało poziom frustracji i dzięki temu dużo chętniej dodaję nowe testy.
AutoMoq
AutoMoq (i jego fork na .net core) to narzędzie, które tworzy obiekty za nas. Wystarczy w parametrze przekazać typ klasy jakiej potrzebujemy, a wszystkie zależności zostaną automatycznie utworzone.
Załóżmy, że mamy klasę:
public class SampleClass
{
private readonly INestedClass _nestedClass;
private readonly IOtherNestedClass _otherNestedClass;
public SampleClass(INestedClass nestedClass, IOtherNestedClass otherNestedClass)
{
_nestedClass = nestedClass ?? throw new ArgumentNullException();
_otherNestedClass = otherNestedClass ?? throw new ArgumentNullException();
}
// ...
}
public interface INestedClass
{
// ...
}
public interface IOtherNestedClass
{
// ...
}
W normalnej sytuacji musielibyśmy stworzyć mock dla interfejsu INestedClass oraz IOtherNestedClass, a następnie przy pomocy tych mocków stworzyć obiekt klasy SampleClass, np.:
var nestedClassMock = new Mock<INestedClass>();
var otherNestedClassMock = new Mock<IOtherNestedClass>();
var sampleClass = new SampleClass(nestedClassMock.Object, otherNestedClassMock.Object);
Dzięki bibliotece AutoMoq wystarczy, że napiszemy coś takiego:
var mocker = new AutoMoqer();
var sampleClass = mocker.Create<SampleClass>();
Mocki dla interfejsów INestedClass oraz IOtherNestedClass zostaną utworzone automatycznie.
Teraz gdybyśmy rozszerzyli naszą klasę SampleClass o kolejne zależności albo byśmy jakieś zależności zmienili, to nie musielibyśmy poprawiać wszystkich testów, a jedynie te dla których faktycznie była to kluczowa zmiana.
Wykorzystanie mocków
AutoMoq automatycznie stworzył nam mocki, ale jak możemy ich użyć? Załóżmy, że nasz interface INestedClass ma metodę GetValue:
public interface INestedClass
{
int GetValue(string text);
// ...
}
Dodatkowo ta metoda jest wykorzystywana w klasie SampleClass w metodzie GetValue:
public int GetValue(string text)
{
return _nestedClass.GetValue(text);
}
Test dla takiej metody może wyglądać następująco:
// Arrange
var mocker = new AutoMoqer();
var nestedClassMock = mocker.GetMock<INestedClass>();
nestedClassMock.Setup(s => s.GetValue(It.IsAny<string>())).Returns(1);
var sampleClass = mocker.Create<SampleClass>();
// Act
var result = sampleClass.GetValue("test");
// Assert
result.ShouldBe(1);
nestedClassMock.Verify(v => v.GetValue(It.Is<string>(p => p == "test")), Times.Once);
Mock obiektu możemy pobrać przy pomocy metody GetMock<>.
Alternatywne biblioteki
Alternatywnymi bibliotekami do AutoMoqu są min.: Moq.AutoMocker i AutoFixture.AutoMoq. Wszystkie 3 biblioteki pod spodem korzystają z Moq i kod tworzony przy ich pomocy jest bardzo podobny.
Pingback: dotnetomaniak.pl
Możliwość komentowania została wyłączona.