Usando o Moq e olhei, Callback
mas não consegui encontrar um exemplo simples para entender como usá-lo.
Você tem um pequeno trecho de trabalho que explica claramente como e quando usá-lo?
Usando o Moq e olhei, Callback
mas não consegui encontrar um exemplo simples para entender como usá-lo.
Você tem um pequeno trecho de trabalho que explica claramente como e quando usá-lo?
Respostas:
Difícil de vencer https://github.com/Moq/moq4/wiki/Quickstart
Se isso não estiver claro o suficiente, eu chamaria isso de bug de doc ...
EDITAR: Em resposta ao seu esclarecimento ...
Para cada método simulado que Setup
você executa, pode indicar coisas como:
O .Callback
mecanismo diz "Não consigo descrever agora, mas quando uma chamada com esse formato acontecer, me ligue de volta e farei o que for preciso". Como parte da mesma cadeia de chamadas fluente, você controla o resultado a ser retornado (se houver) por meio de .Returns
". Nos exemplos de QS, um exemplo é que eles fazem o valor retornado aumentar a cada vez.
Em geral, você não precisará de um mecanismo como este com muita frequência (os padrões de teste xUnit têm termos para antipadrões do tipo Lógica condicional em testes) e, se houver alguma maneira mais simples ou integrada de estabelecer o que você precisa, deve ser usado de preferência.
A parte 3 de 4 da série Moq de Justin Etheredge cobre isso, e há outro exemplo de callbacks aqui
Um exemplo simples de callback pode ser encontrado em Using Callbacks with Moq post.
Callback
não tem nada a ver com o valor de retorno (a menos que você o vincule por meio de código). Basicamente, ele apenas garante que o callback seja chamado antes ou depois de cada invocação (dependendo se você o encadeou antes ou depois, Returns
respectivamente), puro e simples.
Aqui está um exemplo de como usar um retorno de chamada para testar uma entidade enviada a um serviço de dados que lida com uma inserção.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Sintaxe do método genérico alternativo:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Então você pode testar algo como
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
em a em Mock.Verify
vez de sujar o teste com temporários. Mas +1 porque aposto que há muitas pessoas que trabalharão melhor com um exemplo.
Existem dois tipos de Callback
no Moq. Um acontece antes que a chamada retorne; o outro acontece após o retorno da chamada.
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}");
});
Em ambos os retornos de chamada, podemos:
Callback
é simplesmente um meio de executar qualquer código personalizado que você deseja quando uma chamada é feita para um dos métodos do mock. Aqui está um exemplo simples:
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
Recentemente, encontrei um caso de uso interessante para ele. Suponha que você espere algumas chamadas para sua simulação, mas elas acontecem simultaneamente. Portanto, você não tem como saber a ordem em que eles seriam chamados, mas deseja saber as chamadas que esperava que ocorressem (independentemente da ordem). Você pode fazer algo assim:
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
BTW, não se confunda com a enganosa distinção "antes Returns
" e "depois Returns
". É meramente uma distinção técnica se o seu código personalizado será executado após a Returns
avaliação ou antes. Aos olhos do chamador, ambos serão executados antes que o valor seja retornado. Na verdade, se o método for void
-returning, você não pode nem chamar Returns
e ainda funciona da mesma forma. Para obter mais informações, consulte https://stackoverflow.com/a/28727099/67824 .
Além das outras boas respostas aqui, usei-o para realizar a lógica antes de lançar uma exceção. Por exemplo, eu precisava armazenar todos os objetos que foram passados para um método para verificação posterior, e esse método (em alguns casos de teste) precisava lançar uma exceção. Chamando .Throws(...)
no Mock.Setup(...)
substitui a Callback()
ação e nunca chama. No entanto, ao lançar uma exceção dentro do Callback, você ainda pode fazer todas as coisas boas que um callback tem a oferecer e ainda lançar uma exceção.