Add extensibility point for custom auto value providers
More precise set of changes: - Ensure that specification evaluates original argument values, because they could be modified during call dispatch. - Ensure that original arguments are used to check whether current call meets particular specification. - Use own results cache for AutoValues route handler. That is needed to ensure that AutoValues priority isn't increased. Otherwise, once handler invoked for the first time, it's "memory" will have same priority as user configured results. As outcome, custom handlers, returns for type, etc will not be invoked.
This commit is contained in:
Родитель
10fa6bcce8
Коммит
db8c2e0853
|
@ -112,6 +112,9 @@
|
|||
<Compile Include="..\NSubstitute.Acceptance.Specs\ClearSubstitute.cs">
|
||||
<Link>ClearSubstitute.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute.Acceptance.Specs\CustomHandlersSpecs.cs">
|
||||
<Link>CustomHandlersSpecs.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute.Acceptance.Specs\DynamicCalls.cs">
|
||||
<Link>DynamicCalls.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NSubstitute.Core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace NSubstitute.Acceptance.Specs
|
||||
{
|
||||
[TestFixture]
|
||||
public class CustomHandlersSpecs
|
||||
{
|
||||
[Test]
|
||||
public void Value_from_custom_handler_is_returned()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return("42")));
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Value_from_custom_handler_is_returned_for_setup_after_invocation()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
//invoke it before registering handler
|
||||
source.GetValue();
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return("42")));
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Custom_handler_is_called_for_each_time()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
var values = new Queue<string>(new[] { "42", "10" });
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return(values.Dequeue())));
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Configured_call_has_more_priority_than_custom_handler()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return("xxx")));
|
||||
|
||||
source.GetValue().Returns("42");
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Updated_ref_parameter_doesnt_affect_call_specification()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
//Configure our handler to update "ref" argument value
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
call =>
|
||||
{
|
||||
if (call.GetMethodInfo().Name != nameof(IValueSource.GetValueWithRef))
|
||||
return RouteAction.Continue();
|
||||
|
||||
var args = call.GetArguments();
|
||||
args[0] = "refArg";
|
||||
|
||||
return RouteAction.Return("xxx");
|
||||
}));
|
||||
|
||||
string refValue = "ref";
|
||||
source.GetValueWithRef(ref refValue).Returns("42");
|
||||
|
||||
//act
|
||||
refValue = "ref";
|
||||
var result = source.GetValueWithRef(ref refValue);
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Set_out_parameter_doesnt_affect_call_specification()
|
||||
{
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
//Configure our handler to update "out" argument value
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
call =>
|
||||
{
|
||||
if (call.GetMethodInfo().Name != nameof(IValueSource.GetValueWithOut))
|
||||
return RouteAction.Continue();
|
||||
|
||||
var args = call.GetArguments();
|
||||
args[0] = "outArg";
|
||||
|
||||
return RouteAction.Return("xxx");
|
||||
}));
|
||||
|
||||
string outArg;
|
||||
source.GetValueWithOut(out outArg).Returns("42");
|
||||
|
||||
//act
|
||||
string otherOutArg;
|
||||
var result = source.GetValueWithOut(out otherOutArg);
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Is_not_called_for_specifying_call()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
bool wasInvoked = false;
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(_ =>
|
||||
{
|
||||
wasInvoked = true;
|
||||
return RouteAction.Continue();
|
||||
}));
|
||||
|
||||
//act
|
||||
source.MethodWithArgs(Arg.Any<string>(), Arg.Is("42")).Returns("");
|
||||
|
||||
//assert
|
||||
Assert.That(wasInvoked, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Auto_value_is_returned_if_skipped()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(_ => RouteAction.Continue()));
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void First_added_handler_has_precedence()
|
||||
{
|
||||
//arrange
|
||||
var source = Substitute.For<IValueSource>();
|
||||
var router = SubstitutionContext.Current.GetCallRouterFor(source);
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return("42")));
|
||||
|
||||
router.RegisterCustomCallHandlerFactory(state =>
|
||||
new ActionHandler(
|
||||
_ => RouteAction.Return("10")));
|
||||
|
||||
//act
|
||||
var result = source.GetValue();
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
|
||||
public interface IValueSource
|
||||
{
|
||||
string GetValue();
|
||||
string GetValueWithRef(ref string arg1);
|
||||
string GetValueWithOut(out string arg1);
|
||||
string MethodWithArgs(string arg1, string arg2);
|
||||
}
|
||||
|
||||
private class ActionHandler : ICallHandler
|
||||
{
|
||||
private readonly Func<ICall, RouteAction> _handler;
|
||||
|
||||
public ActionHandler(Func<ICall, RouteAction> handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public RouteAction Handle(ICall call) => _handler.Invoke(call);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using NSubstitute.Exceptions;
|
||||
using NUnit.Framework;
|
||||
|
||||
|
@ -9,7 +10,7 @@ namespace NSubstitute.Acceptance.Specs
|
|||
{
|
||||
bool TryRef(ref int number);
|
||||
bool TryGet(string key, out string value);
|
||||
string Get(string key);
|
||||
string GetWithRef(ref string key);
|
||||
string LookupByObject(ref object obj);
|
||||
}
|
||||
|
||||
|
@ -138,6 +139,116 @@ namespace NSubstitute.Acceptance.Specs
|
|||
Assert.That(something.GetValue("diff key"), Is.EqualTo("none"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Modified_ref_arg_should_not_affect_specification_match()
|
||||
{
|
||||
//arrange
|
||||
var lookup = Substitute.For<ILookupStrings>();
|
||||
|
||||
int magicInt = 42;
|
||||
lookup.TryRef(ref magicInt).Returns(true);
|
||||
|
||||
int anyInt = Arg.Any<int>();
|
||||
lookup.When(l => l.TryRef(ref anyInt)).Do(c => { c[0] = 100; });
|
||||
|
||||
//act
|
||||
magicInt = 42;
|
||||
var result = lookup.TryRef(ref magicInt);
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(magicInt, Is.EqualTo(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Modified_out_arg_should_not_affect_specification_match()
|
||||
{
|
||||
//arrange
|
||||
var lookup = Substitute.For<ILookupStrings>();
|
||||
|
||||
string key = "key";
|
||||
|
||||
//Configure to have result for out parameter
|
||||
string retValue;
|
||||
lookup.TryGet(key, out retValue).Returns(true);
|
||||
|
||||
//Set parameter in When/Do to always initialize out parameter
|
||||
string retValueTmp;
|
||||
lookup.When(l => l.TryGet(key, out retValueTmp)).Do(c => { c[1] = "42"; });
|
||||
|
||||
//act
|
||||
//Check whether our configuration is still actual
|
||||
string actualRet;
|
||||
var result = lookup.TryGet(key, out actualRet);
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(actualRet, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Configuration_for_already_configured_call_works()
|
||||
{
|
||||
//arrange
|
||||
var lookup = Substitute.For<ILookupStrings>();
|
||||
|
||||
string anyRef = Arg.Any<string>();
|
||||
lookup.GetWithRef(ref anyRef).Returns(c => { c[0] = "42"; return "result"; });
|
||||
|
||||
//act
|
||||
string otherKey = "xxx";
|
||||
lookup.GetWithRef(ref otherKey).Returns("100");
|
||||
|
||||
otherKey = "xxx";
|
||||
var actualResult = lookup.GetWithRef(ref otherKey);
|
||||
|
||||
//assert
|
||||
Assert.That(actualResult, Is.EqualTo("100"));
|
||||
Assert.That(otherKey, Is.EqualTo("xxx"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Configuration_for_modified_args_works()
|
||||
{
|
||||
//arrange
|
||||
var lookup = Substitute.For<ILookupStrings>();
|
||||
|
||||
string anyRef = Arg.Any<string>();
|
||||
lookup.When(l => l.GetWithRef(ref anyRef)).Do(c => { c[0] = "42"; });
|
||||
|
||||
//act
|
||||
string specRef = "xxx";
|
||||
lookup.GetWithRef(ref specRef).Returns("100");
|
||||
|
||||
specRef = "xxx";
|
||||
var result = lookup.GetWithRef(ref specRef);
|
||||
|
||||
//assert
|
||||
Assert.That(result, Is.EqualTo("100"));
|
||||
Assert.That(specRef, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Exception_message_displays_original_values()
|
||||
{
|
||||
//arrange
|
||||
var lookup = Substitute.For<ILookupStrings>();
|
||||
|
||||
string anyRef = Arg.Any<string>();
|
||||
lookup.When(l => l.GetWithRef(ref anyRef)).Do(c => { c[0] = "42"; });
|
||||
|
||||
//act
|
||||
string key = "12345";
|
||||
lookup.GetWithRef(ref key);
|
||||
|
||||
//assert
|
||||
//Assert that message is like "Expected '98765', but received '12345'".
|
||||
key = "98765";
|
||||
var exception = Assert.Throws<ReceivedCallsException>(() => lookup.Received().GetWithRef(ref key));
|
||||
StringAssert.Contains("98765", exception.Message);
|
||||
StringAssert.Contains("12345", exception.Message);
|
||||
}
|
||||
|
||||
private class Something
|
||||
{
|
||||
private readonly ILookupStrings _lookup;
|
||||
|
|
|
@ -294,6 +294,9 @@
|
|||
<Compile Include="..\NSubstitute\Core\ConfiguredCall.cs">
|
||||
<Link>Core\ConfiguredCall.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Core\CustomHandlers.cs">
|
||||
<Link>Core\CustomHandlers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Core\DefaultForType.cs">
|
||||
<Link>Core\DefaultForType.cs</Link>
|
||||
</Compile>
|
||||
|
@ -360,6 +363,9 @@
|
|||
<Compile Include="..\NSubstitute\Core\IConfigureCall.cs">
|
||||
<Link>Core\IConfigureCall.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Core\ICustomHandlers.cs">
|
||||
<Link>Core\ICustomHandlers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Core\IDefaultForType.cs">
|
||||
<Link>Core\IDefaultForType.cs</Link>
|
||||
</Compile>
|
||||
|
@ -651,6 +657,9 @@
|
|||
<Compile Include="..\NSubstitute\Routing\Handlers\ReturnFromBaseIfRequired.cs">
|
||||
<Link>Routing\Handlers\ReturnFromBaseIfRequired.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Routing\Handlers\ReturnFromCustomHandlers.cs">
|
||||
<Link>Routing\Handlers\ReturnFromCustomHandlers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\NSubstitute\Routing\Handlers\ReturnResultForTypeHandler.cs">
|
||||
<Link>Routing\Handlers\ReturnResultForTypeHandler.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace NSubstitute.Specs
|
|||
var call = mock<ICall>();
|
||||
call.stub(x => x.GetMethodInfo()).Return(methodInfo);
|
||||
call.stub(x => x.GetArguments()).Return(arguments);
|
||||
call.stub(x => x.GetOriginalArguments()).Return(arguments);
|
||||
call.stub(x => x.GetArgumentSpecifications()).Return(mock<IList<IArgumentSpecification>>());
|
||||
return call;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@ namespace NSubstitute.Specs.Infrastructure
|
|||
{
|
||||
return new ConfiguredCall(x => { });
|
||||
}
|
||||
|
||||
public void RegisterCustomCallHandlerFactory(CallHandlerFactory factory)
|
||||
{
|
||||
}
|
||||
|
||||
public void Clear(ClearOptions clear) { }
|
||||
|
||||
public IEnumerable<ICall> ReceivedCalls() { return new ICall[0]; }
|
||||
|
|
|
@ -16,7 +16,9 @@ namespace NSubstitute.Specs.Routing.Handlers
|
|||
protected IAutoValueProvider _secondAutoValueProvider;
|
||||
protected RouteAction _result;
|
||||
protected ICall _call;
|
||||
protected IConfigureCall ConfigureCall;
|
||||
protected ICallResults _callResults;
|
||||
protected ICallSpecification _callSpecification;
|
||||
protected ICallSpecificationFactory _callSpecificationFactory;
|
||||
|
||||
public override void Because()
|
||||
{
|
||||
|
@ -26,16 +28,19 @@ namespace NSubstitute.Specs.Routing.Handlers
|
|||
public override void Context()
|
||||
{
|
||||
base.Context();
|
||||
ConfigureCall = mock<IConfigureCall>();
|
||||
_call = mock<ICall>();
|
||||
_call.stub(x => x.GetReturnType()).Return(_type);
|
||||
_callResults = mock<ICallResults>();
|
||||
_callSpecification = mock<ICallSpecification>();
|
||||
_callSpecificationFactory = mock<ICallSpecificationFactory>();
|
||||
_callSpecificationFactory.stub(x => x.CreateFrom(_call, MatchArgs.AsSpecifiedInCall)).Return(_callSpecification);
|
||||
_firstAutoValueProvider = mock<IAutoValueProvider>();
|
||||
_secondAutoValueProvider = mock<IAutoValueProvider>();
|
||||
}
|
||||
|
||||
public ReturnAutoValue CreateReturnAutoValue(AutoValueBehaviour autoValueBehaviour)
|
||||
{
|
||||
return new ReturnAutoValue(autoValueBehaviour, new[] {_firstAutoValueProvider, _secondAutoValueProvider}, ConfigureCall);
|
||||
return new ReturnAutoValue(autoValueBehaviour, new[] {_firstAutoValueProvider, _secondAutoValueProvider}, _callResults, _callSpecificationFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +57,7 @@ namespace NSubstitute.Specs.Routing.Handlers
|
|||
[Test]
|
||||
public void Should_set_auto_value_as_value_to_return_for_subsequent_calls()
|
||||
{
|
||||
ConfigureCall.received(x => x.SetResultForCall(It.Is(_call), It.Matches<IReturn>(arg => arg.ReturnFor(null) == _autoValue), It.Is(MatchArgs.AsSpecifiedInCall)));
|
||||
_callResults.received(x => x.SetResult(It.Is(_callSpecification), It.Matches<IReturn>(arg => arg.ReturnFor(null) == _autoValue)));
|
||||
}
|
||||
|
||||
public override void Context()
|
||||
|
@ -95,7 +100,7 @@ namespace NSubstitute.Specs.Routing.Handlers
|
|||
[Test]
|
||||
public void Should_set_auto_value_as_value_to_return_for_subsequent_calls()
|
||||
{
|
||||
ConfigureCall.did_not_receive_with_any_args(x => x.SetResultForCall(null, null, null));
|
||||
_callResults.did_not_receive_with_any_args(x => x.SetResult(null, null));
|
||||
}
|
||||
|
||||
public override void Context()
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace NSubstitute.Core
|
|||
{
|
||||
private readonly MethodInfo _methodInfo;
|
||||
private readonly object[] _arguments;
|
||||
private readonly object[] _originalArguments;
|
||||
private readonly object _target;
|
||||
private readonly IParameterInfo[] _parameterInfos;
|
||||
private readonly IList<IArgumentSpecification> _argumentSpecifications;
|
||||
|
@ -21,6 +22,7 @@ namespace NSubstitute.Core
|
|||
{
|
||||
_methodInfo = methodInfo;
|
||||
_arguments = arguments;
|
||||
_originalArguments = arguments.ToArray();
|
||||
_target = target;
|
||||
_parameterInfos = GetParameterInfosFrom(_methodInfo);
|
||||
_argumentSpecifications = argumentSpecsForCall;
|
||||
|
@ -31,6 +33,7 @@ namespace NSubstitute.Core
|
|||
{
|
||||
_methodInfo = methodInfo;
|
||||
_arguments = arguments;
|
||||
_originalArguments = arguments.ToArray();
|
||||
_target = target;
|
||||
_parameterInfos = parameterInfos ?? GetParameterInfosFrom(_methodInfo);
|
||||
_argumentSpecifications = (_parameterInfos.Length == 0) ? EmptyList() : SubstitutionContext.Current.DequeueAllArgumentSpecifications();
|
||||
|
@ -92,6 +95,11 @@ namespace NSubstitute.Core
|
|||
return _arguments;
|
||||
}
|
||||
|
||||
public object[] GetOriginalArguments()
|
||||
{
|
||||
return _originalArguments;
|
||||
}
|
||||
|
||||
public object Target()
|
||||
{
|
||||
return _target;
|
||||
|
|
|
@ -95,5 +95,12 @@ namespace NSubstitute.Core
|
|||
{
|
||||
_substituteState.ResultsForType.SetResult(type, returnValue);
|
||||
}
|
||||
|
||||
public void RegisterCustomCallHandlerFactory(CallHandlerFactory factory)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
_substituteState.CustomHandlers.AddCustomHandlerFactory(factory);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,9 +73,9 @@ namespace NSubstitute.Core
|
|||
&& a.Name.Equals(b.Name, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public IEnumerable<ArgumentMatchInfo> NonMatchingArguments(ICall call)
|
||||
public IEnumerable<ArgumentMatchInfo> NonMatchingArguments(ICall call)
|
||||
{
|
||||
var arguments = call.GetArguments();
|
||||
var arguments = call.GetOriginalArguments();
|
||||
return arguments
|
||||
.Select((arg, index) => new ArgumentMatchInfo(index, arg, _argumentSpecifications[index]))
|
||||
.Where(x => !x.IsMatch);
|
||||
|
@ -89,7 +89,7 @@ namespace NSubstitute.Core
|
|||
|
||||
public string Format(ICall call)
|
||||
{
|
||||
return CallFormatter.Format(call.GetMethodInfo(), FormatArguments(call.GetArguments()));
|
||||
return CallFormatter.Format(call.GetMethodInfo(), FormatArguments(call.GetOriginalArguments()));
|
||||
}
|
||||
|
||||
private IEnumerable<string> FormatArguments(IEnumerable<object> arguments)
|
||||
|
@ -122,7 +122,7 @@ namespace NSubstitute.Core
|
|||
|
||||
private bool HasDifferentNumberOfArguments(ICall call)
|
||||
{
|
||||
return _argumentSpecifications.Length != call.GetArguments().Length;
|
||||
return _argumentSpecifications.Length != call.GetOriginalArguments().Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace NSubstitute.Core
|
|||
{
|
||||
var methodInfo = call.GetMethodInfo();
|
||||
var argumentSpecs = call.GetArgumentSpecifications();
|
||||
var arguments = call.GetArguments();
|
||||
var arguments = call.GetOriginalArguments();
|
||||
var parameterInfos = call.GetParameterInfos();
|
||||
var argumentSpecificationsForCall = _argumentSpecificationsFactory.Create(argumentSpecs, arguments, parameterInfos, matchArgs);
|
||||
return new CallSpecification(methodInfo, argumentSpecificationsForCall);
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NSubstitute.Core
|
||||
{
|
||||
public class CustomHandlers : ICustomHandlers
|
||||
{
|
||||
private readonly List<ICallHandler> _handlers = new List<ICallHandler>();
|
||||
private readonly ISubstituteState _substituteState;
|
||||
|
||||
public IEnumerable<ICallHandler> Handlers => _handlers;
|
||||
|
||||
public CustomHandlers(ISubstituteState substituteState)
|
||||
{
|
||||
_substituteState = substituteState;
|
||||
}
|
||||
|
||||
public void AddCustomHandlerFactory(CallHandlerFactory factory)
|
||||
{
|
||||
_handlers.Add(factory.Invoke(_substituteState));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace NSubstitute.Core
|
|||
Type GetReturnType();
|
||||
MethodInfo GetMethodInfo();
|
||||
object[] GetArguments();
|
||||
object[] GetOriginalArguments();
|
||||
object Target();
|
||||
IParameterInfo[] GetParameterInfos();
|
||||
IList<IArgumentSpecification> GetArgumentSpecifications();
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace NSubstitute.Core
|
|||
IEnumerable<ICall> ReceivedCalls();
|
||||
void SetRoute(Func<ISubstituteState, IRoute> getRoute);
|
||||
void SetReturnForType(Type type, IReturn returnValue);
|
||||
void RegisterCustomCallHandlerFactory(CallHandlerFactory factory);
|
||||
void Clear(ClearOptions clear);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NSubstitute.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory method which creates <see cref="ICallHandler" /> from the <see cref="ISubstituteState" />.
|
||||
/// </summary>
|
||||
public delegate ICallHandler CallHandlerFactory(ISubstituteState substituteState);
|
||||
|
||||
public interface ICustomHandlers
|
||||
{
|
||||
IEnumerable<ICallHandler> Handlers { get; }
|
||||
void AddCustomHandlerFactory(CallHandlerFactory factory);
|
||||
}
|
||||
}
|
|
@ -16,8 +16,10 @@ namespace NSubstitute.Core
|
|||
IConfigureCall ConfigureCall { get; }
|
||||
IEventHandlerRegistry EventHandlerRegistry { get; }
|
||||
IAutoValueProvider[] AutoValueProviders { get; }
|
||||
ICallResults AutoValuesCallResults { get; }
|
||||
ICallBaseExclusions CallBaseExclusions { get; }
|
||||
IResultsForType ResultsForType { get; }
|
||||
ICustomHandlers CustomHandlers { get; }
|
||||
void ClearUnusedCallSpecs();
|
||||
}
|
||||
}
|
|
@ -17,7 +17,9 @@ namespace NSubstitute.Core
|
|||
public IConfigureCall ConfigureCall { get; private set; }
|
||||
public IEventHandlerRegistry EventHandlerRegistry { get; private set; }
|
||||
public IAutoValueProvider[] AutoValueProviders { get; private set; }
|
||||
public ICallResults AutoValuesCallResults { get; }
|
||||
public IResultsForType ResultsForType { get; private set; }
|
||||
public ICustomHandlers CustomHandlers { get; }
|
||||
|
||||
public SubstituteState(ISubstitutionContext substitutionContext, SubstituteConfig option)
|
||||
{
|
||||
|
@ -31,13 +33,13 @@ namespace NSubstitute.Core
|
|||
ReceivedCalls = callStack;
|
||||
PendingSpecification = new PendingSpecification();
|
||||
CallResults = new CallResults(callInfoFactory);
|
||||
AutoValuesCallResults = new CallResults(callInfoFactory);
|
||||
CallSpecificationFactory = CallSpecificationFactoryFactoryYesThatsRight.CreateCallSpecFactory();
|
||||
CallActions = new CallActions(callInfoFactory);
|
||||
CallBaseExclusions = new CallBaseExclusions();
|
||||
ResultsForType = new ResultsForType(callInfoFactory);
|
||||
|
||||
CustomHandlers = new CustomHandlers(this);
|
||||
var getCallSpec = new GetCallSpec(callStack, PendingSpecification, CallSpecificationFactory, CallActions);
|
||||
|
||||
ConfigureCall = new ConfigureCall(CallResults, CallActions, getCallSpec);
|
||||
EventHandlerRegistry = new EventHandlerRegistry();
|
||||
AutoValueProviders = new IAutoValueProvider[] {
|
||||
|
|
|
@ -14,18 +14,25 @@ namespace NSubstitute.Routing.Handlers
|
|||
public class ReturnAutoValue : ICallHandler
|
||||
{
|
||||
private readonly IEnumerable<IAutoValueProvider> _autoValueProviders;
|
||||
private readonly ICallResults _callResults;
|
||||
private readonly ICallSpecificationFactory _callSpecificationFactory;
|
||||
private readonly AutoValueBehaviour _autoValueBehaviour;
|
||||
private readonly IConfigureCall ConfigureCall;
|
||||
|
||||
public ReturnAutoValue(AutoValueBehaviour autoValueBehaviour, IEnumerable<IAutoValueProvider> autoValueProviders, IConfigureCall configureCall)
|
||||
public ReturnAutoValue(AutoValueBehaviour autoValueBehaviour, IEnumerable<IAutoValueProvider> autoValueProviders, ICallResults callResults, ICallSpecificationFactory callSpecificationFactory)
|
||||
{
|
||||
_autoValueProviders = autoValueProviders;
|
||||
ConfigureCall = configureCall;
|
||||
_callResults = callResults;
|
||||
_callSpecificationFactory = callSpecificationFactory;
|
||||
_autoValueBehaviour = autoValueBehaviour;
|
||||
}
|
||||
|
||||
public RouteAction Handle(ICall call)
|
||||
{
|
||||
if (_callResults.HasResultFor(call))
|
||||
{
|
||||
return RouteAction.Return(_callResults.GetResult(call));
|
||||
}
|
||||
|
||||
var type = call.GetReturnType();
|
||||
var compatibleProviders = _autoValueProviders.Where(x => x.CanProvideValueFor(type)).FirstOrNothing();
|
||||
return compatibleProviders.Fold(
|
||||
|
@ -40,7 +47,8 @@ namespace NSubstitute.Routing.Handlers
|
|||
var valueToReturn = provider.GetValue(type);
|
||||
if (_autoValueBehaviour == AutoValueBehaviour.UseValueForSubsequentCalls)
|
||||
{
|
||||
ConfigureCall.SetResultForCall(call, new ReturnValue(valueToReturn), MatchArgs.AsSpecifiedInCall);
|
||||
var spec = _callSpecificationFactory.CreateFrom(call, MatchArgs.AsSpecifiedInCall);
|
||||
_callResults.SetResult(spec, new ReturnValue(valueToReturn));
|
||||
}
|
||||
return RouteAction.Return(valueToReturn);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using NSubstitute.Core;
|
||||
|
||||
namespace NSubstitute.Routing.Handlers
|
||||
{
|
||||
public class ReturnFromCustomHandlers : ICallHandler
|
||||
{
|
||||
private readonly ICustomHandlers _customHandlers;
|
||||
|
||||
public ReturnFromCustomHandlers(ICustomHandlers customHandlers)
|
||||
{
|
||||
_customHandlers = customHandlers;
|
||||
}
|
||||
public RouteAction Handle(ICall call)
|
||||
{
|
||||
foreach (var handler in _customHandlers.Handlers)
|
||||
{
|
||||
var result = handler.Handle(call);
|
||||
if (result.HasReturnValue)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return RouteAction.Continue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace NSubstitute.Routing
|
|||
new ClearUnusedCallSpecHandler(state)
|
||||
, new AddCallToQueryResultHandler(state.SubstitutionContext, state.CallSpecificationFactory)
|
||||
, new ReturnConfiguredResultHandler(state.CallResults)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.ConfigureCall)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
|
||||
, ReturnDefaultForReturnTypeHandler()
|
||||
});
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace NSubstitute.Routing
|
|||
new ClearLastCallRouterHandler(state.SubstitutionContext)
|
||||
, new ClearUnusedCallSpecHandler(state)
|
||||
, new CheckReceivedCallsHandler(state.ReceivedCalls, state.CallSpecificationFactory, new ReceivedCallsExceptionThrower(), matchArgs, requiredQuantity)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.ReturnAndForgetValue, state.AutoValueProviders, state.ConfigureCall)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.ReturnAndForgetValue, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
|
||||
, ReturnDefaultForReturnTypeHandler()
|
||||
});
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace NSubstitute.Routing
|
|||
new RecordCallSpecificationHandler(state.PendingSpecification, state.CallSpecificationFactory, state.CallActions)
|
||||
, new PropertySetterHandler(new PropertyHelper(), state.ConfigureCall)
|
||||
, new ReturnConfiguredResultHandler(state.CallResults)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.ConfigureCall)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
|
||||
, new ReturnFromAndConfigureDynamicCall(state.ConfigureCall)
|
||||
, ReturnDefaultForReturnTypeHandler()
|
||||
});
|
||||
|
@ -75,7 +75,8 @@ namespace NSubstitute.Routing
|
|||
, new ReturnConfiguredResultHandler(state.CallResults)
|
||||
, new ReturnResultForTypeHandler(state.ResultsForType)
|
||||
, new ReturnFromBaseIfRequired(state.SubstituteConfig, state.CallBaseExclusions)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.ConfigureCall)
|
||||
, new ReturnFromCustomHandlers(state.CustomHandlers)
|
||||
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
|
||||
, new ReturnFromAndConfigureDynamicCall(state.ConfigureCall)
|
||||
, ReturnDefaultForReturnTypeHandler()
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче