I am writing C# unit tests using NUnit and NSubstitute. I am testing a class which will attempt to retrieve objects from a config provider implementing the following interface:
public interface IConfigProvider<T> {
T GetConfig(int id);
T GetConfig(string id);
}
The class being tested only uses the int version of GetConfig
so in the SetUpFixture I do the following to set up a mocked config provider that will always return the same dummy object:
IConfigProvider<ConfigType> configProvider = Substitute.For<IConfigProvider<ConfigType>>();
configProvider.GetConfig(Arg.Any<int>()).Returns<ConfigType>(new ConfigType(/* args */);
This runs absolutely fine if that TestFixture is the only one being run. However, in a different TestFixture in the same assembly, I check for received calls like this:
connection.Received(1).SetCallbacks(Arg.Any<Action<Message>>(), Arg.Any<Action<long>>(), Arg.Any<Action<long, Exception>>());
If these Received
tests run before the config provider tests, then the config tests fail in the SetUpFixture with an 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()
What's really confusing me is that I can observe this effect even between test runs - if I use the NUnit GUI to run the Received
tests alone, and then run the config tests alone, the config tests will fail. If I then immediately run the config tests again, they will pass.
Things I've tried:
- Adding
configProvider.GetConfig(Arg.Any<string>()).Returns...
as well, in case the overloading was the problem. - I've read the NSubstitute docs on argument matching, but I can't find a solution there. If it is a case of having to supply argument matchers for both the int and string versions of the method, I can't work out how to do that.
As it happens, the tests I'm using will only ever call the GetConfig
method with values of 0 or 1, so I can just provide Returns
specifications for those two values and not use matching at all, but I want to understand how to fix this more generally.