Using Moq and looked at Callback
but I have not been able to find a simple example to understand how to use it.
Do you have a small working snippet whic
On top of the other good answers here, I've used it to perform logic before throwing an exception. For instance, I needed to store all objects that were passed to a method for later verification, and that method (in some test cases) needed to throw an exception. Calling .Throws(...)
on Mock.Setup(...)
overrides the Callback()
action and never calls it. However, by throwing an exception within the Callback, you can still do all of the good stuff that a callback has to offer, and still throw an exception.
There are two types of Callback
in Moq. One happens before the call returns; the other happens after the call returns.
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 both callbacks, we can:
Hard to beat https://github.com/Moq/moq4/wiki/Quickstart
If that's not clear enough, I'd call that a doc bug...
EDIT: In response to your clarification...
For each mocked method Setup
you perform, you get to indicate things like:
The .Callback
mechanism says "I can't describe it right now, but when a call shaped like this happens, call me back and I'll do what needs to be done". As part of the same fluent call chain, you get to control the result to return (if any) via .Returns
". In the QS examples, an example is that they make the value being returned increase each time.
In general, you won't need a mechanism like this very often (xUnit Test Patterns have terms for antipatterns of the ilk Conditional Logic In Tests), and if there's any simpler or built-in way to establish what you need, it should be used in preference.
Part 3 of 4 in Justin Etheredge's Moq series covers it, and there's another example of callbacks here
A simple example of a callback can be found at Using Callbacks with Moq post.
Callback
is simply a means to execute any custom code you want when a call is made to one of the mock's methods. Here's a simple example:
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
I recently ran into an interesting use case for it. Suppose you expect some calls to your mock, but they happen concurrently. So you have no way of knowing the order in which they'd get called, but you want to know the calls you expected did take place (irrespective of order). You can do something like this:
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 don't get confused by the misleading "before Returns
" and "after Returns
" distinction. It is merely a technical distinction of whether your custom code will run after Returns
has been evaluated or before. In the eyes of the caller, both will run before the value is returned. Indeed, if the method is void
-returning you can't even call Returns
and yet it works the same. For more information see https://stackoverflow.com/a/28727099/67824.
Here's an example of using a callback to test an entity sent to a Data Service that handles an insert.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Alternative generic method syntax:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Then you can test something like
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");