Używając Moq i przyjrzałem się, Callback
ale nie byłem w stanie znaleźć prostego przykładu, aby zrozumieć, jak go używać.
Czy masz mały działający fragment, który jasno wyjaśnia, jak i kiedy go używać?
Odpowiedzi:
Trudno pokonać https://github.com/Moq/moq4/wiki/Quickstart
Jeśli to nie jest wystarczająco jasne, nazwałbym to błędem dokumentacji ...
EDYCJA: W odpowiedzi na Twoje wyjaśnienie ...
W przypadku każdej Setup
wykonywanej przez ciebie fałszywej metody możesz wskazać takie rzeczy, jak:
.Callback
Mechanizm mówi „nie mogę opisać to teraz, ale gdy połączenie kształcie to się dzieje, zadzwoń do mnie z powrotem i zrobię, co trzeba zrobić”. Jako część tego samego łańcucha płynnych wywołań, możesz kontrolować wynik do zwrócenia (jeśli istnieje) za pośrednictwem .Returns
". W przykładach QS, przykładem jest to, że zwiększają one zwracaną wartość za każdym razem.
Ogólnie rzecz biorąc, nie będziesz potrzebował takiego mechanizmu bardzo często (wzorce testowe xUnit mają określenia dla antywzorów podobnych do testów warunkowej logiki w testach), a jeśli istnieje prostszy lub wbudowany sposób ustalenia potrzeb, powinien być używane w pierwszej kolejności.
Część 3 z 4 serii Moq Justina Etheredge'a obejmuje to, a tutaj jest inny przykład wywołań zwrotnych
Prosty przykład oddzwonienia można znaleźć na stronie Korzystanie z oddzwonienia z postem Moq .
Callback
nie ma nic wspólnego z wartością zwracaną (chyba że zdarzy ci się połączyć ją za pomocą kodu). Zasadniczo zapewnia tylko, że wywołanie zwrotne zostanie wywołane przed lub po każdym wywołaniu (w zależności od tego, czy połączyłeś je odpowiednio przed, czy po Returns
), proste i proste.
Oto przykład użycia wywołania zwrotnego w celu przetestowania jednostki wysłanej do usługi danych, która obsługuje wstawianie.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Alternatywna składnia metody ogólnej:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Następnie możesz przetestować coś takiego
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
w a Mock.Verify
zamiast zaśmiecania testu tymczasowymi. Ale +1, bo założę się, że na przykładzie wielu ludzi będzie działać najlepiej.
Istnieją dwa rodzaje Callback
w Moq. Jeden zdarza się przed powrotem połączenia; druga ma miejsce po powrocie połączenia.
var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
.Callback((x, y) =>
{
message = "Rally on!";
Console.WriteLine($"args before returns {x} {y}");
})
.Returns(message) // Rally on!
.Callback((x, y) =>
{
message = "Rally over!";
Console.WriteLine("arg after returns {x} {y}");
});
W obu callbackach możemy:
Callback
jest po prostu środkiem do wykonania dowolnego kodu niestandardowego, który chcesz, gdy wywoływana jest jedna z metod makiety. Oto prosty przykład:
public interface IFoo
{
int Bar(bool b);
}
var mock = new Mock<IFoo>();
mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
.Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
.Returns(42);
var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);
// output:
// Bar called with: True
// Result: 42
Niedawno natknąłem się na ciekawy przypadek użycia tego. Przypuśćmy, że spodziewasz się kilku wezwań do swojej sztuczki, ale są one wykonywane jednocześnie. Nie masz więc możliwości poznania kolejności, w jakiej będą dzwonione, ale chcesz wiedzieć, że połączenia, których oczekiwałeś, miały miejsce (niezależnie od kolejności). Możesz zrobić coś takiego:
var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));
// output:
// Invocations: True, False
A tak przy okazji, nie daj się zmylić wprowadzającym w błąd rozróżnieniem „przed Returns
” i „po Returns
”. Jest to jedynie techniczne rozróżnienie tego, czy Twój kod niestandardowy będzie działał po Returns
ocenie, czy wcześniej. W oczach wywołującego obie będą działać przed zwróceniem wartości. Rzeczywiście, jeśli metoda void
powraca, nie możesz nawet zadzwonić, Returns
a mimo to działa tak samo. Więcej informacji można znaleźć pod adresem https://stackoverflow.com/a/28727099/67824 .
Oprócz innych dobrych odpowiedzi, użyłem go do wykonania logiki przed rzuceniem wyjątku. Na przykład musiałem przechowywać wszystkie obiekty, które zostały przekazane do metody w celu późniejszej weryfikacji, a ta metoda (w niektórych przypadkach testowych) musiała zgłosić wyjątek. Dzwoniąc .Throws(...)
na Mock.Setup(...)
Przesłania Callback()
działania i nigdy nie nazywa. Jednak rzucając wyjątek w Callback, nadal możesz zrobić wszystkie dobre rzeczy, które ma do zaoferowania callback, i nadal zgłaszać wyjątek.