NSubstitute - TestFixture 1 вызывает исключение AmbiguousArgumentsException в TestFixture 2
Я пишу блок-тесты С#, используя NUnit и NSubstitute. Я тестирую класс, который попытается извлечь объекты из поставщика конфигурации, реализующего следующий интерфейс:
public interface IConfigProvider<T> {
T GetConfig(int id);
T GetConfig(string id);
}
Проверяемый класс использует только версию int GetConfig
, поэтому в SetUpFixture я делаю следующее, чтобы настроить посредника конфигурации, который всегда будет возвращать один и тот же фиктивный объект:
IConfigProvider<ConfigType> configProvider = Substitute.For<IConfigProvider<ConfigType>>();
configProvider.GetConfig(Arg.Any<int>()).Returns<ConfigType>(new ConfigType(/* args */);
Это работает абсолютно нормально, если TestFixture - единственный, который запускается. Однако в другом TestFixture в той же сборке я проверяю полученные вызовы следующим образом:
connection.Received(1).SetCallbacks(Arg.Any<Action<Message>>(), Arg.Any<Action<long>>(), Arg.Any<Action<long, Exception>>());
Если эти тесты Received
запускаются до тестирования поставщика конфигурации, тогда тесты конфигурации не сработают в SetUpFixture с AmbiguousArgumentsException:
Here.Be.Namespace.ProfileManagerTests+Setup (TestFixtureSetUp):
SetUp : NSubstitute.Exceptions.AmbiguousArgumentsException : Cannot determine argument specifications to use.
Please use specifications for all arguments of the same type.
at NSubstitute.Core.Arguments.NonParamsArgumentSpecificationFactory.Create(Object argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications)
at System.Linq.Enumerable.<SelectIterator>d__7`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at NSubstitute.Core.Arguments.MixedArgumentSpecificationsFactory.Create(IList`1 argumentSpecs, Object[] arguments, IParameterInfo[] parameterInfos)
at NSubstitute.Core.Arguments.ArgumentSpecificationsFactory.Create(IList`1 argumentSpecs, Object[] arguments, IParameterInfo[] parameterInfos, MatchArgs matchArgs)
at NSubstitute.Core.CallSpecificationFactory.CreateFrom(ICall call, MatchArgs matchArgs)
at NSubstitute.Routing.Handlers.RecordCallSpecificationHandler.Handle(ICall call)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at NSubstitute.Routing.Route.Handle(ICall call)
at NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.IConfigProvider`1Proxy.GetConfig(Int32 id)
at Here.Be.Namespace.ProfileManagerTests.Setup.DoSetup()
Что действительно меня смущает, так это то, что я могу наблюдать этот эффект даже между тестовыми запусками - если я использую графический интерфейс NUnit для запуска тестов Received
, а затем запускаю только тесты конфигурации, тесты конфигурации не сработают. Если я затем снова запустим тесты конфигурации, они пройдут.
Вещи, которые я пробовал:
- Добавление
configProvider.GetConfig(Arg.Any<string>()).Returns...
также в случае, если проблема перегрузки была проблемой.
- Я прочитал документы NSubstitute по сопоставлению аргументов, но я не могу найти там решение. Если это случай, когда необходимо предоставить аргументы для обеих версий метода int и string, я не могу решить, как это сделать.
Как это бывает, тесты, которые я использую, будут когда-либо вызывать метод GetConfig
со значениями 0 или 1, поэтому я могу просто предоставить спецификации Returns
для этих двух значений и не использовать совпадение вообще, но Я хочу понять, как это сделать в целом.
Ответы
Ответ 1
Неоднозначные аргументы - это когда NSubstitute сравнивает аргументы с вызовом, с которым он в настоящее время работает, в стек "аргументов", которые он имеет (каждый раз, когда вызывается Arg.Blah
, в этот стек добавляется сопоставление аргументов) и он не может решить, какой аргумент идет туда.
Обычно это вызвано вызовом типа blah(null, null)
, с одиночным совпадением аргументов в очереди, но также может быть вызвано тем, что стек выходит из синхронизации из-за использования сопоставления аргументов вне конфигурации вызова, или в качестве аргумента для не виртуального метода.
Версия 1.8.0 (выпущенная после вашего вопроса) включает в себя несколько улучшенное обнаружение последнего случая, поэтому, возможно, стоит попробовать.
Кроме того, у меня была эта проблема несколько раз и я использовал следующий (болезненный) подход.
- выполните тест отдельно и убедитесь, что он проходит
- определите, какой тест запускается немедленно (обычно можно догадаться, но тестовые журналы могут помочь здесь) и запускать только эти два теста. Подтвердите, что он не работает.
- Ищите любые вызовы
Arg.xyz
, которые могли бы поставить очередь на совпадение аргументов в любом тесте. Убедитесь, что он используется как часть конфигурации вызова. Иногда разработка того, какой вызов является проблематичным, может быть выполнена путем комментирования строк или замены аргументов arg другими значениями.
- Убедитесь, что нет вызовов виртуальных методов, которые запутывают NSubstitute.
Иногда проблема может быть связана с предыдущим крепежом, поэтому вам может потребоваться тренировка предыдущего прибора и его исследование.: (
Ответ 2
I имел схожие ошибки, которые начались, когда я переключил тестер Microsoft на VSTest.Console
(они не выполнялись при работе под MSTest.exe
).
Как было сказано в ответе Дэвида, ошибки были вызваны вызовами незанятых методов с параметрами Arg.*
. Arg.Any
были переданы фактическим методам кода, которые вызываются без Returns
или Received
соответствующих методов.
Чтобы сканировать мою тестовую библиотеку для таких проблем, я использовал поиск с регулярным выражением для поиска строк с Arg.
, но не Arg.
, следующих за Returns
или предшествующих Received
(?=^.*Arg.*$)(?=^((?!Arg.*\.Returns).)*$)^((?!\.Received\(.*Arg.).)*$
Это не защищенный от вирусов фильтр (например, он не исключает многострочные заявления), но он помогает уменьшить количество вызовов для проверки.
Ответ 3
Сменился порядок моих тестов. Не большой ответ, но работали - попробуйте!