Risposte:
Difficile da battere https://github.com/Moq/moq4/wiki/Quickstart
Se non è abbastanza chiaro, lo chiamerei un bug doc ...
EDIT: In risposta al tuo chiarimento ...
Per ogni metodo deriso Setupche esegui, devi indicare cose come:
Il .Callbackmeccanismo dice "Non posso descriverlo in questo momento, ma quando si verifica una chiamata in questo modo, richiamami e farò ciò che deve essere fatto". Come parte della stessa catena di chiamate fluente, puoi controllare il risultato da restituire (se presente) tramite .Returns". Negli esempi QS, un esempio è che fanno aumentare il valore restituito ogni volta.
In generale, non avrai bisogno di un meccanismo come questo molto spesso (xUnit Test Patterns ha termini per antipatterns del genere Conditional Logic In Tests), e se c'è un modo più semplice o integrato per stabilire ciò di cui hai bisogno, dovrebbe essere usato di preferenza.
La parte 3 di 4 nella serie Moq di Justin Etheredge lo copre, e c'è un altro esempio di callback qui
Un semplice esempio di callback può essere trovato in Using Callbacks with Moq post.
Callbacknon ha nulla a che fare con il valore di ritorno (a meno che non ti capiti di collegarlo tramite codice). Fondamentalmente si assicura solo che la richiamata venga chiamata prima o dopo ogni invocazione (a seconda che sia stata concatenata prima o dopo Returnsrispettivamente), semplice e semplice.
Ecco un esempio di utilizzo di una richiamata per testare un'entità inviata a un servizio dati che gestisce un inserimento.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Sintassi alternativa del metodo generico:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Quindi puoi provare qualcosa di simile
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>in a Mock.Verifyinvece di sporcare il test con temps. Ma +1 perché scommetto che ci sono molte persone che funzioneranno meglio da un esempio.
Esistono due tipi di Callbackin Moq. Uno accade prima che la chiamata ritorni; l'altro accade dopo il ritorno della chiamata.
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}");
});
In entrambi i callback possiamo:
Callbackè semplicemente un mezzo per eseguire qualsiasi codice personalizzato che desideri quando viene effettuata una chiamata a uno dei metodi del mock. Ecco un semplice esempio:
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
Di recente mi sono imbattuto in un interessante caso d'uso per questo. Supponi di aspettarti alcune chiamate alla tua finta, ma avvengono contemporaneamente. Quindi non hai modo di conoscere l'ordine in cui verrebbero chiamati, ma vuoi sapere che le chiamate che ti aspettavi hanno avuto luogo (indipendentemente dall'ordine). Puoi fare qualcosa del genere:
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 non lasciatevi confondere dalla distinzione fuorviante "prima Returns" e "dopo Returns". È solo una distinzione tecnica per stabilire se il codice personalizzato verrà eseguito dopo che Returnsè stato valutato o prima. Agli occhi del chiamante, entrambi verranno eseguiti prima che il valore venga restituito. Infatti, se il metodo viene voidrestituito non puoi nemmeno chiamare Returnse tuttavia funziona allo stesso modo. Per ulteriori informazioni, vedere https://stackoverflow.com/a/28727099/67824 .
Oltre alle altre buone risposte qui, l'ho usato per eseguire la logica prima di lanciare un'eccezione. Ad esempio, avevo bisogno di memorizzare tutti gli oggetti passati a un metodo per una verifica successiva e quel metodo (in alcuni casi di test) doveva generare un'eccezione. Chiamata .Throws(...)sul Mock.Setup(...)override l' Callback()azione e non lo chiama. Tuttavia, generando un'eccezione all'interno del callback, puoi ancora fare tutte le cose buone che un callback ha da offrire e comunque lanciare un'eccezione.