Reputation: 21769
I have a test that looks like this:
[Test]
public void Blah()
{
// Arrange
// ...
var thing = new Thing();
mockRouter.Route(Arg.Is<Transition<Thing>>(x => x != null && x.Subject != null && x.Subject.Equals(thing)));
// Act
var result = handler.Handle(thing);
// Assert
mockRouter.Received(1).Route(Arg.Is<Transition<Thing>>(x => x != null && x.Subject != null && x.Subject.Equals(thing)));
}
I would like to cache up the Arg
definition in a local variable so I can reuse it in the assert. The point is to reduce the amount of code in the test and make it read a bit more fluidly.
[Test]
public void Blah()
{
// Arrange
var thing = new Thing();
var transitionForThing = Arg.Is<Transition<Thing>>(x => x != null && x.Subject != null && x.Subject.Equals(thing));
mockRouter.Route(transitionForThing);
// ...
// Act
var result = handler.Handle(thing);
// Assert
mockRouter.Received(1).Route(transitionForThing);
}
This does not seem to work, as the value of transitionForThing
is null, and so the assertion fails saying that Received(null)
was not called. Is there a way to do this or something similar, or am I stuck with this syntax?
Upvotes: 0
Views: 222
Reputation: 10484
The value returned by Arg.Is(...)
and other Arg
methods is of relatively little importance. The important bit is the invocation of the method itself.
Every time Arg.Is(...)
is called NSubstitute takes note that you tried to specify an argument, and uses that information to work out the details of the call you are specifying. If you cache the value (which will just be default(T)
), NSubstitute will not know you want to match the same argument.
I can explain this more if you are interested, but the important thing to note is you need to call Arg.Is(...)
every time you specify a call (and in the correct order expected for the call).
To reuse matcher logic I'd extract the predicate into a new method (as DaniCE suggested), or create a method that calls Arg.Is
and use it with caution.
[Test]
public void Blah() {
var thing = new Thing();
mockRouter.Route(Arg.Is<Transition<Thing>>(x => HasSubject(x, thing)));
// ...
var result = handler.Handle(thing);
// ...
mockRouter.Received(1).Route(Arg.Is<Transition<Thing>>(x => HasSubject(x, thing)));
}
private bool HasSubject(Transition<Thing> x, Thing thing) {
return x != null && x.Subject != null && x.Subject.Equals(thing));
}
//or...
[Test]
public void Blah2() {
var thing = new Thing();
mockRouter.Route(ArgWith(thing));
// ...
var result = handler.Handle(thing);
// ...
mockRouter.Received(1).Route(ArgWith(thing));
}
private Transition<Thing> ArgWith(Thing thing) {
return Arg.Is<Transition<Thing>>(x => x != null && x.Subject != null && x.Subject.Equals(thing));
}
Upvotes: 0
Reputation: 2421
Arg.Is has a parameter of type
Expression<Predicate<T>>
, so you can define it to reuse
Expression<Predicate<Transition<Thing>>> predicate =
x => x != null && x.Subject != null && x.Subject.Equals(thing));
and use it as
mockRouter.Route(predicate);
But i really don't understand your situation: you usually mock classes that return some result that you need. I think in your case you only need to check that the method of the mocked class has been called, you don't need to define a mock for the action.
Upvotes: 1