/// <summary> /// Registers a custom handler for the specified specimen if that is a substitute. /// Custom handler resolves call result and ref/out arguments using the passed <paramref name="context"/>. /// </summary> public void Execute(object specimen, ISpecimenContext context) { if (specimen == null) { throw new ArgumentNullException(nameof(specimen)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } ICallRouter router; try { router = this.SubstitutionContext.GetCallRouterFor(specimen); } catch (NotASubstituteException) { return; } // Add extensibility point for users to allow to use different cache implementation. // For instance users might want to disable results caching, as was proposed here: // https://github.com/AutoFixture/AutoFixture/issues/625 var resultsCacheForSubstitution = this.CallResultCacheFactory.CreateCache(); var callResultsResolver = this.CallResultResolverFactory.Create(context); router.RegisterCustomCallHandlerFactory( substituteState => new AutoFixtureValuesHandler( callResultsResolver, resultsCacheForSubstitution, CompatShim.GetCallSpecificationFactory(substituteState, this.SubstitutionContext))); }
public void ReturnsUsingContext(MethodInfo methodInfo) { // The workflow is following: // 1. We setup callback that lazily configures return value. // 2. When callback is executed for the first time, it sets up own call handlers. // Then we invoke method again to pass control to that handlers. // 3. NoSetupCallbackHandler uses substitute state to check whether we already have result for the call. // That could happen if client already configured return value for particular property/method manually. // Also, that happens if we already configured return value for the call. // // If value is not already present: // 3.1 Resolve result using the AutoFixture. // 3.2 Configure NSubstitute to return that value using the `Returns()` method. // 3.3 Invoke the method again. The points 1-3 will be repeated, // but this time the NoSetupCallbackHandler will do nothing - we have a result. // // // Consider the following things during the code analysis: // - Each time consumer invokes property/method, we set our custom route and pass control to it. // We need that to access substitute state and check whether we already have a result for the call. // // - Each time our route is executed, NSubstitute switches to default route. // - Our route is executed twice during inital setup. Second time it does nothing. // - The initial request (which comes from consumer) is in progress (on stack below) when we do all our configuration. // That is because Do() {} callback is executed before known return values are returned. // After our substitute is configured in When-Do, NSubstitute checks whether there is return value for current call. // Return value is, of course, present and it returns that value to the consumer. this.Substitute .WhenForAnyArgs(_ => this.InvokeMethod(methodInfo)) .Do(callInfo => { var arguments = callInfo.Args(); var callRouter = SubstitutionContext.Current.GetCallRouterFor(this.Substitute); callRouter.SetRoute(state => CompatShim.Route_CreateNew( new ICallHandler[] { new NoSetupCallbackHandler(state, () => { var value = this.Resolve(methodInfo.ReturnType); if (value is OmitSpecimen) { return; } this.ReturnsFixedValue(methodInfo, value); this.InvokeMethod(methodInfo, arguments); }), new ReturnDefaultForReturnTypeHandler( new DefaultForType()) })); this.InvokeMethod(methodInfo, arguments); }); }
public bool HasResultFor(ICall call) { return(CompatShim.CallResults_HasCallResultFor(this.state.CallResults, call)); }