2014-08-27 6 views
0

У меня есть тест, который выглядит следующим образом:Есть ли способ кэшировать определение 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 определение в локальной переменной, так что я могу использовать его в утверждают. Дело в том, чтобы уменьшить количество кода в тесте и сделать его немного более плавным.

[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) не называли. Есть ли способ сделать это или что-то подобное, или я придерживаюсь этого синтаксиса?

ответ

1

Arg.Is имеет параметр типа

Expression<Predicate<T>> 

, так что вы можете определить его для повторного использования

Expression<Predicate<Transition<Thing>>> predicate = 
    x => x != null && x.Subject != null && x.Subject.Equals(thing)); 

и использовать его в качестве

mockRouter.Route(predicate); 

Но я действительно не» Понимаете ли вы свою ситуацию: вы обычно издеваетесь над классами, которые возвращают некоторый результат, который вам нужен. Я думаю, что в вашем случае вам нужно только проверить, вызван ли метод издеваемого класса, вам не нужно определять макет для действия.

+0

Хорошая точка. Я преобразовал тест из Rhino Mocks и повторил некоторое плохое поведение. – Kit

0

Величина, возвращаемая Arg.Is(...) и другими методами Arg, имеет относительно небольшое значение. Важным битом является вызов самого метода.

Каждый раз, когда Arg.Is(...) называется NSubstitute, обратите внимание, что вы пытались указать аргумент и используете эту информацию для определения деталей вызова, который вы указываете. Если вы уберете значение (которое будет только default(T)), NSubstitute не будет знать, что вы хотите соответствовать одному и тому же аргументу.

Я могу explain this more if you are interested, но важно отметить, что вам нужно позвонить Arg.Is(...) каждый раз, когда вы укажете вызов (и в правильном порядке, ожидаемом для вызова).

Чтобы повторно использовать логику сопряжения, я извлечу предикат в новый метод (как DaniCE suggested) или создав метод, который вызывает 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)); 
}