/// <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)));
        }
Example #2
0
            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));
 }