Есть ли способ кэшировать определение Arg.Is<> для использования в частях "Arrange" и "Act" теста?
У меня есть тест, который выглядит так:
[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)));
}
Я хотел бы кешировать Arg
определение в локальной переменной, поэтому я могу использовать его в assert. Суть в том, чтобы уменьшить количество кода в тесте и сделать его более плавным.
[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);
}
Это не похоже на работу, так как значение transitionForThing
является нулевым, и поэтому утверждение не может сказать, что Received(null)
не был назван. Есть ли способ сделать это или что-то подобное, или я застрял с этим синтаксисом?
2 ответа
Arg.Is имеет параметр типа
Expression<Predicate<T>>
так что вы можете определить его для повторного использования
Expression<Predicate<Transition<Thing>>> predicate =
x => x != null && x.Subject != null && x.Subject.Equals(thing));
и использовать его как
mockRouter.Route(predicate);
Но я действительно не понимаю вашу ситуацию: вы обычно высмеиваете классы, которые возвращают какой-то результат, который вам нужен. Я думаю, что в вашем случае вам нужно только проверить, что метод mocked-класса был вызван, вам не нужно определять макет для действия.
Значение, возвращаемое Arg.Is(...)
и другие Arg
методы имеют сравнительно небольшое значение. Важным моментом является вызов самого метода.
Каждый раз Arg.Is(...)
называется NSubstitute, принимает к сведению, что вы попытались указать аргумент, и использует эту информацию для уточнения деталей вызова, который вы указываете. Если вы кешируете значение (которое будет просто default(T)
), NSubstitute не будет знать, что вы хотите сопоставить один и тот же аргумент.
Я могу объяснить это больше, если вам интересно, но важно отметить, что вам нужно позвонить Arg.Is(...)
каждый раз, когда вы указываете вызов (и в правильном порядке, ожидаемом для вызова).
Чтобы повторно использовать логику сопоставления, я бы извлек предикат в новый метод (как предложил DaniCE) или создал метод, который вызывает Arg.Is
и используйте это с осторожностью.
[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));
}