├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── other-issues.md │ └── question.md ├── dependabot.yml └── workflows │ ├── release_documentation.yml │ ├── release_packages.yml │ └── test.yml ├── .gitignore ├── BreakingChanges.md ├── CHANGELOG.md ├── Directory.Build.props ├── LICENSE.txt ├── NSubstitute.sln ├── README.md ├── acknowledgements.md ├── build ├── ExtractDocs.fs ├── build.fs ├── build.fsproj └── build.sln ├── docs ├── docfx.json ├── docs │ ├── 2010-01-01-getting-started.md │ ├── 2010-01-02-creating-a-substitute.md │ ├── 2010-02-01-set-return-value.md │ ├── 2010-02-02-return-for-args.md │ ├── 2010-02-03-return-for-any-args.md │ ├── 2010-02-03-return-from-function.md │ ├── 2010-02-04-multiple-returns.md │ ├── 2010-02-10-replacing-return-values.md │ ├── 2010-03-01-received-calls.md │ ├── 2010-03-10-clear-received-calls.md │ ├── 2010-04-01-argument-matchers.md │ ├── 2010-05-01-callbacks.md │ ├── 2010-05-02-throwing-exceptions.md │ ├── 2010-05-10-configure.md │ ├── 2010-06-01-raising-events.md │ ├── 2010-10-01-auto-and-recursive-mocks.md │ ├── 2010-11-01-setting-out-and-ref-arguments.md │ ├── 2010-12-01-actions-with-arguments.md │ ├── 2013-01-01-received-in-order.md │ ├── 2013-02-01-partial-subs.md │ ├── 2013-03-01-return-for-all.md │ ├── 2013-04-01-threading.md │ ├── 2013-04-15-compat-args.md │ ├── 2013-05-01-nsubstitute-analysers.md │ ├── 2015-01-01-how-nsub-works.md │ ├── 2019-01-01-search.md │ └── toc.yml ├── favicon.ico ├── help.md ├── images │ ├── NSubstitute.ai │ ├── github-16x16.png │ ├── nsubstitute-100x100.png │ ├── nsubstitute-16x16.png │ ├── nsubstitute-32x32.png │ ├── nuget-16x16.png │ ├── teamcity-16x16.png │ └── teamcity-32x32.png ├── index.md └── toc.yml ├── src └── NSubstitute │ ├── Arg.cs │ ├── Callback.cs │ ├── Callbacks │ └── ConfiguredCallback.cs │ ├── ClearOptions.cs │ ├── Compatibility │ ├── Arg.Compat.cs │ ├── CompatArg.cs │ └── DiagnosticsNullabilityAttributes.cs │ ├── Core │ ├── Argument.cs │ ├── ArgumentSpecificationDequeue.cs │ ├── Arguments │ │ ├── AnyArgumentMatcher.cs │ │ ├── ArgumentFormatter.cs │ │ ├── ArgumentMatchInfo.cs │ │ ├── ArgumentMatcher.cs │ │ ├── ArgumentSpecification.cs │ │ ├── ArgumentSpecificationCompatibilityTester.cs │ │ ├── ArgumentSpecificationFactory.cs │ │ ├── ArgumentSpecificationsFactory.cs │ │ ├── ArrayContentsArgumentMatcher.cs │ │ ├── DefaultChecker.cs │ │ ├── EqualsArgumentMatcher.cs │ │ ├── ExpressionArgumentMatcher.cs │ │ ├── IArgumentFormatter.cs │ │ ├── IArgumentMatcher.cs │ │ ├── IArgumentSpecification.cs │ │ ├── IArgumentSpecificationCompatibilityTester.cs │ │ ├── IArgumentSpecificationFactory.cs │ │ ├── IArgumentSpecificationsFactory.cs │ │ ├── IDefaultChecker.cs │ │ ├── ISuppliedArgumentSpecifications.cs │ │ ├── ISuppliedArgumentSpecificationsFactory.cs │ │ ├── SuppliedArgumentSpecifications.cs │ │ └── SuppliedArgumentSpecificationsFactory.cs │ ├── Call.cs │ ├── CallActions.cs │ ├── CallBaseConfiguration.cs │ ├── CallCollection.cs │ ├── CallFactory.cs │ ├── CallFormatter.cs │ ├── CallInfo.cs │ ├── CallInfoFactory.cs │ ├── CallResults.cs │ ├── CallRouter.cs │ ├── CallRouterFactory.cs │ ├── CallRouterResolver.cs │ ├── CallSpecAndTarget.cs │ ├── CallSpecification.cs │ ├── CallSpecificationFactory.cs │ ├── ConfigureCall.cs │ ├── ConfiguredCall.cs │ ├── CustomHandlers.cs │ ├── DefaultForType.cs │ ├── DependencyInjection │ │ ├── INSubContainer.cs │ │ ├── NSubContainer.cs │ │ ├── NSubLifetime.cs │ │ └── NSubstituteDefaultFactory.cs │ ├── EventCallFormatter.cs │ ├── EventHandlerRegistry.cs │ ├── Events │ │ ├── DelegateEventWrapper.cs │ │ ├── EventHandlerWrapper.cs │ │ └── RaiseEventWrapper.cs │ ├── Extensions.cs │ ├── GetCallSpec.cs │ ├── IArgumentSpecificationDequeue.cs │ ├── ICall.cs │ ├── ICallActions.cs │ ├── ICallBaseConfiguration.cs │ ├── ICallCollection.cs │ ├── ICallFactory.cs │ ├── ICallHandler.cs │ ├── ICallInfoFactory.cs │ ├── ICallResults.cs │ ├── ICallRouter.cs │ ├── ICallRouterFactory.cs │ ├── ICallRouterProvider.cs │ ├── ICallRouterResolver.cs │ ├── ICallSpecification.cs │ ├── ICallSpecificationFactory.cs │ ├── IConfigureCall.cs │ ├── ICustomHandlers.cs │ ├── IDefaultForType.cs │ ├── IDescribeNonMatches.cs │ ├── IDescribeSpecification.cs │ ├── IEventHandlerRegistry.cs │ ├── IGetCallSpec.cs │ ├── IMethodInfoFormatter.cs │ ├── IParameterInfo.cs │ ├── IPendingSpecification.cs │ ├── IPropertyHelper.cs │ ├── IProxyFactory.cs │ ├── IQuery.cs │ ├── IQueryResults.cs │ ├── IReceivedCallsExceptionThrower.cs │ ├── IResultsForType.cs │ ├── IReturn.cs │ ├── ISubstituteFactory.cs │ ├── ISubstituteState.cs │ ├── ISubstituteStateFactory.cs │ ├── ISubstitutionContext.cs │ ├── IThreadLocalContext.cs │ ├── MatchArgs.cs │ ├── Maybe.cs │ ├── MethodFormatter.cs │ ├── ParameterInfoWrapper.cs │ ├── PendingSpecificationInfo.cs │ ├── PropertyCallFormatter.cs │ ├── PropertyHelper.cs │ ├── Query.cs │ ├── ReceivedCallsExceptionThrower.cs │ ├── ReflectionExtensions.cs │ ├── ResultsForType.cs │ ├── ReturnObservable.cs │ ├── RobustThreadLocal.cs │ ├── RouteAction.cs │ ├── RouteFactoryCacheWrapper.cs │ ├── SequenceChecking │ │ ├── InstanceTracker.cs │ │ ├── SequenceFormatter.cs │ │ └── SequenceInOrderAssertion.cs │ ├── SequenceNumberGenerator.cs │ ├── SubstituteFactory.cs │ ├── SubstituteState.cs │ ├── SubstituteStateFactory.cs │ ├── SubstitutionContext.cs │ ├── ThreadLocalContext.cs │ └── WhenCalled.cs │ ├── Exceptions │ ├── AmbiguousArgumentsException.cs │ ├── ArgumentIsNotOutOrRefException.cs │ ├── ArgumentNotFoundException.cs │ ├── ArgumentSetWithIncompatibleValueException.cs │ ├── CallSequenceNotFoundException.cs │ ├── CanNotPartiallySubForInterfaceOrDelegateException.cs │ ├── CannotCreateEventArgsException.cs │ ├── CannotReturnNullforValueType.cs │ ├── CouldNotConfigureBaseMethodException.cs │ ├── CouldNotRaiseEventException.cs │ ├── CouldNotSetReturnException.cs │ ├── MissingSequenceNumberException.cs │ ├── NotASubstituteException.cs │ ├── NotRunningAQueryException.cs │ ├── NullSubstituteReferenceException.cs │ ├── ProtectedMethodNotFoundException.cs │ ├── ProtectedMethodNotVirtualException.cs │ ├── ReceivedCallsException.cs │ ├── RedundantArgumentMatcherException.cs │ ├── SubstituteException.cs │ ├── SubstituteInternalException.cs │ ├── TypeForwardingException.cs │ └── UnexpectedArgumentMatcherException.cs │ ├── Extensions │ ├── ClearExtensions.cs │ ├── ConfigurationExtensions.cs │ ├── ExceptionExtensions.cs │ ├── ProtectedExtensions.cs │ ├── ReceivedExtensions.cs │ ├── ReturnsExtensions.cs │ └── ReturnsForAllExtensions.cs │ ├── NSubstitute.csproj │ ├── Proxies │ └── CastleDynamicProxy │ │ ├── CastleDynamicProxyFactory.cs │ │ ├── CastleForwardingInterceptor.cs │ │ ├── CastleInvocationMapper.cs │ │ └── ProxyIdInterceptor.cs │ ├── Raise.cs │ ├── Received.cs │ ├── Routing │ ├── AutoValues │ │ ├── AutoArrayProvider.cs │ │ ├── AutoObservableProvider.cs │ │ ├── AutoQueryableProvider.cs │ │ ├── AutoStringProvider.cs │ │ ├── AutoSubstituteProvider.cs │ │ ├── AutoTaskProvider.cs │ │ ├── AutoValueProvidersFactory.cs │ │ ├── IAutoValueProvider.cs │ │ └── IAutoValueProvidersFactory.cs │ ├── Handlers │ │ ├── AddCallToQueryResultHandler.cs │ │ ├── CallBaseForCallHandler.cs │ │ ├── CheckReceivedCallsHandler.cs │ │ ├── ClearLastCallRouterHandler.cs │ │ ├── ClearUnusedCallSpecHandler.cs │ │ ├── DoActionsCallHandler.cs │ │ ├── DoNotCallBaseForCallHandler.cs │ │ ├── EventSubscriptionHandler.cs │ │ ├── PropertySetterHandler.cs │ │ ├── RaiseEventHandler.cs │ │ ├── RecordCallHandler.cs │ │ ├── RecordCallSpecificationHandler.cs │ │ ├── ReturnAutoValue.cs │ │ ├── ReturnConfiguredResultHandler.cs │ │ ├── ReturnDefaultForReturnTypeHandler.cs │ │ ├── ReturnFromAndConfigureDynamicCall.cs │ │ ├── ReturnFromBaseIfRequired.cs │ │ ├── ReturnFromCustomHandlers.cs │ │ ├── ReturnResultForTypeHandler.cs │ │ ├── SetActionForCallHandler.cs │ │ └── TrackLastCallHandler.cs │ ├── IRoute.cs │ ├── IRouteFactory.cs │ ├── Route.cs │ └── RouteFactory.cs │ ├── Substitute.cs │ ├── SubstituteExtensions.Received.cs │ ├── SubstituteExtensions.Returns.Task.cs │ ├── SubstituteExtensions.Returns.ValueTask.cs │ ├── SubstituteExtensions.Returns.cs │ ├── SubstituteExtensions.When.Task.cs │ ├── SubstituteExtensions.When.ValueTask.cs │ ├── SubstituteExtensions.When.cs │ └── nsubstitute.snk └── tests ├── NSubstitute.Acceptance.Specs ├── ArgDoFromMatcher.cs ├── ArgumentInvocationFromMatchers.cs ├── ArgumentMatching.cs ├── ArgumentMatchingCompat.cs ├── AssemblySigningTest.cs ├── AsyncEventHandlers │ ├── AsyncEventHandlersWithNSubstituteTests.cs │ ├── ITestInterface.cs │ └── TestImplementation.cs ├── AutoValuesForSubs.cs ├── CallbackCalling.cs ├── ClearSubstitute.cs ├── CompatArgsTests.cs ├── ConcurrencyTests.cs ├── ConfigurationExtensionTests.cs ├── CustomHandlersSpecs.cs ├── DynamicCalls.cs ├── EventChecking.cs ├── EventRaising.cs ├── ExceptionsWhenCheckingReceivedCalls.cs ├── ExceptionsWhenCheckingSequencesOfCalls.cs ├── FieldReports │ ├── ArgMatchingWithNestedSubCalls.cs │ ├── ArgMatchingWithValueTypeArgSpecsForObjectArguments.cs │ ├── CallingIntoNewSubWithinReturns.cs │ ├── DisposeWithThreadLocal.cs │ ├── EqualsBehaviourOnClassSubs.cs │ ├── ExceptionsThrownFromCustomArgumentMatchers.cs │ ├── Issue110_CustomExceptions.cs │ ├── Issue111_ArgMatchesWithRefAndOutParams.cs │ ├── Issue114_ArgumentCheckOfOptionalParameter.cs │ ├── Issue118_ConcreteClassWithPublicStaticMethod.cs │ ├── Issue125_MethodWithSealedClassReturnType.cs │ ├── Issue129_AmbiguousArgsWithOutRef.cs │ ├── Issue149_ArgMatcherInReturns.cs │ ├── Issue170_MultidimensionalArray.cs │ ├── Issue211_ReceivedInOrderParamsFormatting.cs │ ├── Issue225_ConfiguredValueIsUsedInSubsequentSetups.cs │ ├── Issue237_ReceivedInOrderErrorHandling.cs │ ├── Issue262_NonPublicSetterCall.cs │ ├── Issue271_DelegateOutArgument.cs │ ├── Issue279_ShouldFailOnRedundantArguments.cs │ ├── Issue282_MultipleReturnValuesParallelism.cs │ ├── Issue291_CannotReconfigureThrowingConfiguration.cs │ ├── Issue33_RaisingINotifyPropertyChangedEvents.cs │ ├── Issue372_InterfaceSameNameOfMethods.cs │ ├── Issue378_InValueTypes.cs │ ├── Issue38_SettingNullReturnValue.cs │ ├── Issue423_SetVirtualMembersInConstructor.cs │ ├── Issue45_CallInfoArgAccessFailsForNull.cs │ ├── Issue47_RaisingEventsWithNullArg.cs │ ├── Issue500_SpecialMethodsWithoutAttribute.cs │ ├── Issue515_OutOfRangeExceptionForNestedGenericTypes.cs │ ├── Issue533_TaskExceptionMightBeSwallowed.cs │ ├── Issue560_RaiseEventWithArrayArg.cs │ ├── Issue569_QueryShouldNotInvokeConfiguredResult.cs │ ├── Issue577_CannotSetOutValue.cs │ ├── Issue57_SettingVirtualPropertyInCtor.cs │ ├── Issue59_ArgDoWithReturns.cs │ ├── Issue61_ArgAnyStringRegression.cs │ ├── Issue631_NamespaceDelegate.cs │ ├── Issue75_DoesNotWorkWithMembersThatUseDynamic.cs │ ├── Issue77_EqualsBehaviourOnClassStubs.cs │ ├── Issue83_MethodsWithGenericStructConstraint.cs │ ├── Issue_RaiseEventOnNonSubstitute.cs │ ├── MigratingToCompatArgs.cs │ ├── Regression_ReceivedClearsStub.cs │ ├── StaticStateBleeding.cs │ └── SubbingSynchronizationContext.cs ├── FormattingCallsWhenThrowingReceivedCallsExceptions.cs ├── GenericArguments.cs ├── ILoggerTests.cs ├── Infrastructure │ ├── AnotherClass.cs │ ├── BackgroundTask.cs │ ├── FluentSomething.cs │ ├── IFluentSomething.cs │ ├── ISomething.cs │ ├── ISomethingWithGenericMethods.cs │ ├── PendingAttribute.cs │ └── SomeClass.cs ├── MatchingDerivedTypesForGenerics.cs ├── MultipleThreads.cs ├── NSubContainerTests.cs ├── NSubstitute.Acceptance.Specs.csproj ├── NotASubstituteExceptions.cs ├── NullReferenceCheckingForSubstituteExtensions.cs ├── OutAndRefParameters.cs ├── PartialSubExamples.cs ├── PartialSubs.cs ├── PerfTests.cs ├── PropertyBehaviour.cs ├── ProtectedExtensionsTests.cs ├── ProxyIdentification.cs ├── ReceivedCalls.cs ├── RecursiveSubs.cs ├── ReturningResults.cs ├── ReturnsAndDoes.cs ├── ReturnsForAll.cs ├── ReturnsForAllFromFunc.cs ├── SequenceChecking.cs ├── SimpleSubstituteExamples.cs ├── SubbingForConcreteTypesAndMultipleInterfaces.cs ├── SubbingForEventHandler.cs ├── SubstituteTimingAndInteractions.cs ├── SubstitutingForDelegates.cs ├── ThrowingAsyncExceptions.cs ├── ThrowingExceptions.cs ├── TypeForwarding.cs └── WhenCalledDo.cs ├── NSubstitute.Benchmarks ├── ActivationBenchmark.cs ├── ArgumentSpecificationUsageBenchmark.cs ├── DispatchConfiguredMatchingCallBenchmark.cs ├── DispatchConfiguredNonMatchingCallBenchmark.cs ├── DispatchNonConfiguredCallBenchmark.cs ├── NSubstitute.Benchmarks.csproj ├── Program.cs ├── TestTypes │ ├── AbstractClassWithSingleMethod.cs │ ├── ClassWithSingleMethod.cs │ ├── ClassWithToStringImplementation.cs │ ├── Delegates.cs │ ├── IInterfaceWithMultipleMethods.cs │ └── IInterfaceWithSingleMethod.cs ├── ToStringCallBenchmark.cs └── VerifyReceivedCallBenchmark.cs └── NSubstitute.Specs ├── AnonymousObserver.cs ├── ArgumentSpecs.cs ├── Arguments ├── ArgumentSpecificationFactorySpecs.cs ├── ArgumentSpecificationSpecs.cs ├── ArgumentSpecificationsFactorySpecs.cs ├── ArrayArgumentSpecificationsFactorySpecs.cs ├── ArrayContentsArgumentMatcherSpecs.cs ├── DefaultCheckerSpecs.cs ├── EqualsArgumentMatcherSpecs.cs ├── ExpressionArgumentMatcherSpecs.cs ├── MixedArgumentSpecificationsFactorySpecs.cs ├── NonParamsArgumentSpecificationFactorySpecs.cs ├── ParameterInfosFromParamsArrayFactorySpecs.cs ├── ParamsArgumentSpecificationFactorySpecs.cs ├── SuppliedArgumentSpecificationsFactorySpecs.cs └── SuppliedArgumentSpecificationsSpecs.cs ├── CallActionsSpecs.cs ├── CallBaseExclusionsSpecs.cs ├── CallCollectionSpecs.cs ├── CallFactorySpecs.cs ├── CallFormatterSpecs.cs ├── CallInfoFactorySpecs.cs ├── CallInfoSpecs.cs ├── CallResultsSpecs.cs ├── CallRouterResolverSpecs.cs ├── CallRouterSpecs.cs ├── CallSpecificationFactorySpecs.cs ├── CallSpecificationSpecs.cs ├── ClearSubstituteExtensionSpec.cs ├── ConfigureCallSpecs.cs ├── DefaultForTypeSpecs.cs ├── DidNotReceiveExtensionsSpecs.cs ├── DidNotReceiveWithAnyArgsExtensionSpec.cs ├── EventHandlerRegistrySpec.cs ├── ExceptionsSpecs.cs ├── ExtensionsSpecs.cs ├── GetCallSpecSpecs.cs ├── Infrastructure ├── BaseConcern.cs ├── ConcernFor.cs ├── ITemporaryChange.cs ├── MockingAdaptor.cs ├── ReflectionHelper.cs ├── StaticConcern.cs ├── TemporaryChange.cs ├── TemporaryChangeNotConfiguredProperlyException.cs ├── TemporaryChangeToBuilder.cs ├── TestCallRouter.cs └── Tests │ ├── BaseConcernSpecs.cs │ └── ReflectionHelperSpecs.cs ├── NSubstitute.Specs.csproj ├── PendingSpecificationSpecs.cs ├── Properties └── AssemblyInfo.cs ├── PropertyHelperSpecs.cs ├── Proxies ├── CastleDynamicProxy │ └── CastleDynamicProxyFactorySpecs.cs ├── DelegateProxy │ └── DelegateProxyFactorySpecs.cs └── ProxyFactorySpecs.cs ├── QuantitySpecs.cs ├── ReceivedCallsExceptionThrowerSpecs.cs ├── ReceivedCallsExtensionSpec.cs ├── ReceivedExtensionSpec.cs ├── ReceivedWithAnyArgsExtensionSpec.cs ├── ReflectionExtensionsSpecs.cs ├── ResultsForTypeSpec.cs ├── ReturnExtensionSpec.cs ├── ReturnValueFromFuncSpec.cs ├── ReturnsForAllExtensionSpecs.cs ├── ReturnsForAllFuncExtensionSpecs.cs ├── Routing ├── AutoValues │ ├── AutoArrayProviderSpecs.cs │ ├── AutoQueryableProviderSpecs.cs │ ├── AutoStringProviderSpecs.cs │ ├── AutoSubstituteProviderSpecs.cs │ └── AutoTaskProviderSpecs.cs ├── Handlers │ ├── AddCallToQueryResultHandlerSpecs.cs │ ├── CheckReceivedCallHandlerSpecs.cs │ ├── ClearLastCallRouterHandlerSpecs.cs │ ├── DoActionsCallHandlerSpecs.cs │ ├── EventSubscriptionHandlerSpec.cs │ ├── PropertySetterHandlerSpecs.cs │ ├── RaiseEventHandlerSpec.cs │ ├── RecordCallHandlerSpecs.cs │ ├── ReturnAutoValueSpecs.cs │ ├── ReturnConfiguredResultHandlerSpecs.cs │ ├── ReturnDefaultForReturnTypeHandlerSpecs.cs │ ├── ReturnResultForTypeHandlerSpecs.cs │ └── SetActionsForCallHandlerSpecs.cs └── RouteSpecs.cs ├── SampleStructures ├── Foo.cs └── IFoo.cs ├── SequenceChecking └── InstanceTrackerSpecs.cs ├── SubstituteFactorySpecs.cs ├── SubstituteSpecs.cs ├── SubstitutionContextSpecs.cs └── WhenCalledSpecs.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | trim_trailing_whitespace = true 5 | 6 | [*.{cs,fs,fsx}] 7 | indent_size = 4 8 | indent_style = space 9 | 10 | [*.{sln,csproj,fsproj,yml,props}] 11 | indent_size = 2 12 | indent_style = space 13 | 14 | [*.cs] 15 | # Disable "this." keyword qualification in code 16 | dotnet_style_qualification_for_field = false:suggestion 17 | dotnet_style_qualification_for_property = false:suggestion 18 | dotnet_style_qualification_for_method = false:suggestion 19 | dotnet_style_qualification_for_event = false:suggestion 20 | csharp_style_namespace_declarations = file_scoped:warning 21 | dotnet_style_prefer_collection_expression = true:suggestion 22 | dotnet_style_collection_initializer = true:warning 23 | csharp_style_prefer_primary_constructors = true:warning 24 | 25 | # ReSharper properties 26 | resharper_int_align_switch_expressions = true 27 | resharper_keep_existing_invocation_parens_arrangement = false 28 | resharper_space_within_single_line_array_initializer_braces = true 29 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # These files are text and should be normalized (convert crlf => lf) 2 | *.cs text diff=csharp 3 | *.xaml text 4 | *.csproj text 5 | *.sln text 6 | *.tt text 7 | *.ps1 text 8 | *.cmd text 9 | *.msbuild text 10 | *.md text 11 | 12 | # Images should be treated as binary 13 | # (binary is a macro for -text -diff) 14 | *.png binary 15 | *.jpeg binary 16 | *.exe binary 17 | *.dll binary 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | An example to help us reproduce the behaviour. (If you'd like some tips on the kind of information to include have a look at StackOverflow's [creating a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve).) 12 | 13 | Please make sure you are using [NSubstitute.Analyzers](https://nsubstitute.github.io/help/nsubstitute-analysers/) and that it does not pick up any problems with the reproduction code. The analyzers can help detect the cause of many issues. 14 | 15 | **Expected behaviour** 16 | A clear and concise description of what you expected to happen, compared to what actually happened. 17 | 18 | **Environment:** 19 | - NSubstitute version: [e.g. 3.1.0] 20 | - NSubstitute.Analyzers version: [e.g. CSharp 1.0.9] 21 | - Platform: [e.g. dotnetcore2.0 project on Mac] 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other issues 3 | about: Anything that doesn't quite fit in the other catergories :) 4 | 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask about how to do a specific task or how something works 4 | 5 | --- 6 | 7 | **Question** 8 | Ask away! If you are trying to do a specific task (e.g. how do I mock this event?), please include a code example to help us understand the context and potentially provide a sample answer. (StackOverflow's guide to [creating a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) may help with the sort of information to include.) 9 | 10 | **Related links** 11 | If you've already asked or found related questions on [StackOverflow](https://stackoverflow.com/questions/tagged/nsubstitute) please include links here: 12 | * link 1 13 | * link 2 14 | -------------------------------------------------------------------------------- /.github/workflows/release_documentation.yml: -------------------------------------------------------------------------------- 1 | name: Release documentation 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | build: 7 | runs-on: windows-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: | 16 | 9.0.x 17 | 8.0.x 18 | 19 | - name: Setup docfx 20 | run: dotnet tool install -g docfx 21 | 22 | - name: Build documentation 23 | run: docfx build 24 | working-directory: ./docs 25 | 26 | - name: Upload documentation 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: docs 30 | path: | 31 | ./docs/_site 32 | retention-days: 7 33 | compression-level: 9 34 | -------------------------------------------------------------------------------- /.github/workflows/release_packages.yml: -------------------------------------------------------------------------------- 1 | name: Release packages 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | build: 7 | runs-on: windows-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: 9.0.x 16 | 17 | - name: Build package 18 | run: dotnet pack src/NSubstitute/NSubstitute.csproj -p:CI=true 19 | 20 | - name: Upload packages 21 | uses: actions/upload-artifact@v4 22 | with: 23 | name: packages 24 | path: | 25 | bin/Release/NSubstitute/*.nupkg 26 | bin/Release/NSubstitute/*.snupkg 27 | retention-days: 7 -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test, and Format verification 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: [windows-latest, ubuntu-latest, macOS-latest] 13 | framework: [net9.0, net8.0] 14 | include: 15 | - os: windows-latest 16 | framework: net462 17 | 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v4 25 | with: 26 | dotnet-version: | 27 | 9.0.x 28 | 8.0.x 29 | 30 | - name: Build 31 | run: dotnet build 32 | 33 | - name: Test 34 | run: dotnet test -f ${{ matrix.framework }} --no-build --no-restore 35 | 36 | test-documentation: 37 | runs-on: windows-latest 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | 42 | - name: Setup .NET 43 | uses: actions/setup-dotnet@v4 44 | with: 45 | dotnet-version: | 46 | 9.0.x 47 | 8.0.x 48 | 49 | - name: Build documentation 50 | run: dotnet run --project 'build/build.fsproj' 51 | 52 | format-verify: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - name: Checkout 56 | uses: actions/checkout@v4 57 | 58 | - name: Setup .NET 59 | uses: actions/setup-dotnet@v4 60 | with: 61 | dotnet-version: 9.0.x 62 | 63 | - name: Format 64 | run: dotnet format --verify-no-changes -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | latest 9 | enable 10 | 11 | 12 | 13 | $(MSBuildThisFileDirectory)\bin\$(Configuration)\$(MSBuildProjectName)\ 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Anthony Egerton (nsubstitute@delfish.com) and David Tchepak (dave@davesquared.net) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the names of the copyright holders nor the names of 13 | contributors may be used to endorse or promote products derived from this 14 | software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | [ https://www.opensource.org/licenses/bsd-license.php ] -------------------------------------------------------------------------------- /build/build.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | Exe 6 | buildOutput 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /build/build.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34525.116 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "build", "build.fsproj", "{80930547-418C-49D9-9966-2A578389957C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {80930547-418C-49D9-9966-2A578389957C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {80930547-418C-49D9-9966-2A578389957C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {80930547-418C-49D9-9966-2A578389957C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {80930547-418C-49D9-9966-2A578389957C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {60C493F8-CB37-405C-8D66-8A05D7537755} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /docs/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", 3 | "metadata": [ 4 | { 5 | "src": [ 6 | { 7 | "src": "../src", 8 | "files": [ 9 | "**/*.csproj" 10 | ] 11 | } 12 | ], 13 | "dest": "api" 14 | } 15 | ], 16 | "build": { 17 | "content": [ 18 | { 19 | "files": [ 20 | "**/*.{md,yml}" 21 | ], 22 | "exclude": [ 23 | "_site/**" 24 | ] 25 | } 26 | ], 27 | "resource": [ 28 | { 29 | "files": [ 30 | "images/**", 31 | "favicon.ico" 32 | ] 33 | } 34 | ], 35 | "output": "_site", 36 | "template": [ 37 | "default", 38 | "modern" 39 | ], 40 | "globalMetadata": { 41 | "_appName": "NSubstitute", 42 | "_appTitle": "NSubstitute", 43 | "_appLogoPath": "images/nsubstitute-32x32.png", 44 | "_enableSearch": true, 45 | "pdf": true, 46 | "repo": "https://github.com/nsubstitute/NSubstitute" 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /docs/docs/2010-02-02-return-for-args.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Return for specific args 3 | --- 4 | 5 | 15 | 16 | Return values can be configured for different combinations of arguments passed to calls using [argument matchers](/help/argument-matchers). This topic is covered in more detail in the [Argument matchers](/help/argument-matchers) entry, but the following examples show the general idea. 17 | 18 | ```csharp 19 | //Return when first arg is anything and second arg is 5: 20 | calculator.Add(Arg.Any(), 5).Returns(10); 21 | Assert.AreEqual(10, calculator.Add(123, 5)); 22 | Assert.AreEqual(10, calculator.Add(-9, 5)); 23 | Assert.AreNotEqual(10, calculator.Add(-9, -9)); 24 | 25 | //Return when first arg is 1 and second arg less than 0: 26 | calculator.Add(1, Arg.Is(x => x < 0)).Returns(345); 27 | Assert.AreEqual(345, calculator.Add(1, -2)); 28 | Assert.AreNotEqual(345, calculator.Add(1, 2)); 29 | 30 | //Return when both args equal to 0: 31 | calculator.Add(Arg.Is(0), Arg.Is(0)).Returns(99); 32 | Assert.AreEqual(99, calculator.Add(0, 0)); 33 | ``` -------------------------------------------------------------------------------- /docs/docs/2010-02-03-return-for-any-args.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Return for any args 3 | --- 4 | 5 | 15 | 16 | A call can be configured to return a value regardless of the arguments passed using the `ReturnsForAnyArgs()` extension method. 17 | 18 | ```csharp 19 | calculator.Add(1, 2).ReturnsForAnyArgs(100); 20 | Assert.AreEqual(100, calculator.Add(1, 2)); 21 | Assert.AreEqual(100, calculator.Add(-7, 15)); 22 | ``` 23 | 24 | **Tip!** You can also use the `default` C# keyword for better readability: 25 | 26 | ```csharp 27 | calculator.Add(default, default).ReturnsForAnyArgs(100); 28 | ``` 29 | 30 | The same behaviour can also be achieved using [argument matchers](/help/argument-matchers): it is simply a shortcut for replacing each argument with `Arg.Any()`. 31 | 32 | `ReturnsForAnyArgs()` has the same overloads as `Returns()`, so you can also specify multiple return values or calculated return values using this approach. -------------------------------------------------------------------------------- /docs/docs/2010-02-04-multiple-returns.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple return values 3 | --- 4 | 5 | 15 | 16 | A call can also be configured to return a different value over multiple calls. The following example shows this for a call to a property, but it works the same way for method calls. 17 | 18 | ```csharp 19 | calculator.Mode.Returns("DEC", "HEX", "BIN"); 20 | Assert.AreEqual("DEC", calculator.Mode); 21 | Assert.AreEqual("HEX", calculator.Mode); 22 | Assert.AreEqual("BIN", calculator.Mode); 23 | ``` 24 | 25 | This can also be achieved by [returning from a function](/help/return-from-function), but passing multiple values to `Returns()` is simpler and reads better. 26 | 27 | ## Multiple returns using callbacks 28 | 29 | `Returns()` also supports passing multiple [functions to return from](/help/return-from-function), which allows one call in a sequence to throw an exception or perform some other action. 30 | 31 | ```csharp 32 | calculator.Mode.Returns(x => "DEC", x => "HEX", x => { throw new Exception(); }); 33 | Assert.AreEqual("DEC", calculator.Mode); 34 | Assert.AreEqual("HEX", calculator.Mode); 35 | Assert.Throws(() => { var result = calculator.Mode; }); 36 | ``` 37 | 38 | ## Configuring other calls without using up multiple returns 39 | 40 | If a call has been configured with multiple returns values, you can configure a more specific call without using up any of these callbacks using [`.Configure()`](/help/configure/). -------------------------------------------------------------------------------- /docs/docs/2010-02-10-replacing-return-values.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Replacing return values 3 | --- 4 | 5 | The return value for a method or property can be set as many times as required. Only the most recently set value will be returned. 6 | 7 | ```csharp 8 | calculator.Mode.Returns("DEC,HEX,OCT"); 9 | calculator.Mode.Returns(x => "???"); 10 | calculator.Mode.Returns("HEX"); 11 | calculator.Mode.Returns("BIN"); 12 | Assert.AreEqual(calculator.Mode, "BIN"); 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /docs/docs/2010-03-10-clear-received-calls.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Clearing received calls 3 | --- 4 | 5 | A substitute can forget all the calls previously made to it using the `ClearReceivedCalls()` extension method. 6 | 7 | Say we have an `ICommand` interface, and we want a `OnceOffCommandRunner` that will take an `ICommand` and only run it once. 8 | 9 | ```csharp 10 | public interface ICommand { 11 | void Execute(); 12 | } 13 | 14 | public class OnceOffCommandRunner { 15 | ICommand command; 16 | public OnceOffCommandRunner(ICommand command) { 17 | this.command = command; 18 | } 19 | public void Run() { 20 | if (command == null) return; 21 | command.Execute(); 22 | command = null; 23 | } 24 | } 25 | ``` 26 | 27 | If we substitute for `ICommand` we can test it is called on the first run, then forget any previous calls made to it, and make sure it is not called again. 28 | 29 | ```csharp 30 | var command = Substitute.For(); 31 | var runner = new OnceOffCommandRunner(command); 32 | 33 | //First run 34 | runner.Run(); 35 | command.Received().Execute(); 36 | 37 | //Forget previous calls to command 38 | command.ClearReceivedCalls(); 39 | 40 | //Second run 41 | runner.Run(); 42 | command.DidNotReceive().Execute(); 43 | ``` 44 | 45 | `ClearReceivedCalls()` will not clear any results set up for the substitute using `Returns()`. If we need to this, we can [replace previously specified results](/help/replacing-return-values) by calling `Returns()` again. -------------------------------------------------------------------------------- /docs/docs/2010-05-02-throwing-exceptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Throwing exceptions 3 | --- 4 | 5 | 12 | 13 | The `Throws` and `ThrowsAsync` helpers in the `NSubstitute.ExceptionExtensions` namespace can be used to throw exceptions when a member is called. 14 | 15 | ```csharp 16 | //For non-voids: 17 | calculator.Add(-1, -1).Throws(new Exception()); // Or .Throws() 18 | 19 | //For voids and non-voids: 20 | calculator 21 | .When(x => x.Add(-2, -2)) 22 | .Throw(x => new Exception()); // Or .Throw() - - don't use .Throw*s* in this case 23 | 24 | //Both calls will now throw. 25 | Assert.Throws(() => calculator.Add(-1, -1)); 26 | Assert.Throws(() => calculator.Add(-2, -2)); 27 | ``` 28 | 29 | ### Returns 30 | Another way is to use the underlying method, `.Returns`. See also [Callbacks](/help/callbacks). 31 | 32 | ```csharp 33 | //For non-voids: 34 | calculator.Add(-1, -1).Returns(x => { throw new Exception(); }); 35 | 36 | //For voids and non-voids: 37 | calculator 38 | .When(x => x.Add(-2, -2)) 39 | .Do(x => { throw new Exception(); }); 40 | 41 | //Both calls will now throw. 42 | Assert.Throws(() => calculator.Add(-1, -1)); 43 | Assert.Throws(() => calculator.Add(-2, -2)); 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/docs/2013-04-01-threading.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Threading 3 | --- 4 | 5 | It is fairly standard for production code to call a substitute from multiple threads, but we should avoid having our test code configure or assert on a substitute while it is also be used from other threads in production code. 6 | 7 | Although this particular issue has been mitigated by work in [#452]({{ site.repo }}/pull/462), issue [#256]({{ site.repo }}/issues/256) shows the types of problems that can occur if we're not careful with threading. 8 | 9 | To avoid this sort of problem, make sure your test has finished configuring its substitutes before exercising the production code, then make sure the production code has completed before your test asserts on `Received()` calls. -------------------------------------------------------------------------------- /docs/docs/2019-01-01-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Search 3 | --- 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /docs/docs/toc.yml: -------------------------------------------------------------------------------- 1 | - href: 2010-01-01-getting-started.md 2 | - href: 2010-01-02-creating-a-substitute.md 3 | - href: 2010-02-01-set-return-value.md 4 | - href: 2010-02-02-return-for-args.md 5 | - href: 2010-02-03-return-for-any-args.md 6 | - href: 2010-02-03-return-from-function.md 7 | - href: 2010-02-04-multiple-returns.md 8 | - href: 2010-02-10-replacing-return-values.md 9 | - href: 2010-03-01-received-calls.md 10 | - href: 2010-03-10-clear-received-calls.md 11 | - href: 2010-04-01-argument-matchers.md 12 | - href: 2010-05-01-callbacks.md 13 | - href: 2010-05-02-throwing-exceptions.md 14 | - href: 2010-05-10-configure.md 15 | - href: 2010-06-01-raising-events.md 16 | - href: 2010-10-01-auto-and-recursive-mocks.md 17 | - href: 2010-11-01-setting-out-and-ref-arguments.md 18 | - href: 2010-12-01-actions-with-arguments.md 19 | - href: 2013-01-01-received-in-order.md 20 | - href: 2013-02-01-partial-subs.md 21 | - href: 2013-03-01-return-for-all.md 22 | - href: 2013-04-01-threading.md 23 | - href: 2013-04-15-compat-args.md 24 | - href: 2013-05-01-nsubstitute-analysers.md 25 | - href: 2015-01-01-how-nsub-works.md 26 | - href: 2019-01-01-search.md 27 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/favicon.ico -------------------------------------------------------------------------------- /docs/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docs and getting help 3 | permalink: /help.html 4 | --- 5 | 6 |

Read Getting started for a quick tour of NSubstitute.

7 | 8 |

For more in depth information start with Creating a substitute.

9 | 10 |

If you can't find the answer you're looking for, or if you have feature requests or feedback on NSubstitute, please raise an issue on our project site. All questions are welcome via our project site, but for "how-to"-style questions you can also try StackOverflow with the [nsubstitute] tag, which often leads to very good answers from the larger programming community. StackOverflow is especially useful if your question also relates to other libraries that our team may not be as familiar with (e.g. NSubstitute with Entity Framework).

11 | -------------------------------------------------------------------------------- /docs/images/NSubstitute.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/NSubstitute.ai -------------------------------------------------------------------------------- /docs/images/github-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/github-16x16.png -------------------------------------------------------------------------------- /docs/images/nsubstitute-100x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/nsubstitute-100x100.png -------------------------------------------------------------------------------- /docs/images/nsubstitute-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/nsubstitute-16x16.png -------------------------------------------------------------------------------- /docs/images/nsubstitute-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/nsubstitute-32x32.png -------------------------------------------------------------------------------- /docs/images/nuget-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/nuget-16x16.png -------------------------------------------------------------------------------- /docs/images/teamcity-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/teamcity-16x16.png -------------------------------------------------------------------------------- /docs/images/teamcity-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/docs/images/teamcity-32x32.png -------------------------------------------------------------------------------- /docs/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Documentation 2 | href: docs/ 3 | 4 | - name: Getting help 5 | href: help.md 6 | 7 | - name: NSubstitute on GitHub 8 | href: https://github.com/nsubstitute/NSubstitute -------------------------------------------------------------------------------- /src/NSubstitute/ClearOptions.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute; 2 | 3 | [Flags] 4 | public enum ClearOptions 5 | { 6 | /// 7 | /// Clear all the received calls 8 | /// 9 | ReceivedCalls = 1, 10 | 11 | /// 12 | /// Clear all configured return results (including auto-substituted values). 13 | /// 14 | ReturnValues = 2, 15 | 16 | /// 17 | /// Clear all call actions configured for this substitute (via When..Do, Arg.Invoke, and Arg.Do) 18 | /// 19 | CallActions = 4, 20 | 21 | /// 22 | /// Clears all received calls and configured return values and callbacks. 23 | /// 24 | All = ReceivedCalls | ReturnValues | CallActions 25 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Argument.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class Argument(ICall call, int argIndex) 4 | { 5 | private readonly ICall? _call = call; 6 | 7 | public object? Value 8 | { 9 | get => _call!.GetArguments()[argIndex]; 10 | set => _call!.GetArguments()[argIndex] = value; 11 | } 12 | 13 | public bool IsByRef => DeclaredType.IsByRef; 14 | 15 | public Type DeclaredType => _call!.GetParameterInfos()[argIndex].ParameterType; 16 | 17 | public Type ActualType => Value == null ? DeclaredType : Value.GetType(); 18 | 19 | public bool IsDeclaredTypeEqualToOrByRefVersionOf(Type type) => 20 | AsNonByRefType(DeclaredType) == type; 21 | 22 | public bool IsValueAssignableTo(Type type) => 23 | type.IsAssignableFrom(AsNonByRefType(ActualType)); 24 | 25 | public bool CanSetValueWithInstanceOf(Type type) => 26 | AsNonByRefType(DeclaredType).IsAssignableFrom(type); 27 | 28 | private static Type AsNonByRefType(Type type) => 29 | type.IsByRef ? type.GetElementType()! : type; 30 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ArgumentSpecificationDequeue.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core.Arguments; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class ArgumentSpecificationDequeue(Func> dequeueAllQueuedArgSpecs) : IArgumentSpecificationDequeue 6 | { 7 | private static readonly IArgumentSpecification[] EmptySpecifications = []; 8 | 9 | public IList DequeueAllArgumentSpecificationsForMethod(int parametersCount) 10 | { 11 | if (parametersCount == 0) 12 | { 13 | // We violate public contract, as mutable list was expected as result. 14 | // However, in reality we never expect value to be mutated, so this optimization is fine. 15 | // We are not allowed to change public contract due to SemVer, so keeping that as it is. 16 | return EmptySpecifications; 17 | } 18 | 19 | var queuedArgSpecifications = dequeueAllQueuedArgSpecs.Invoke(); 20 | return queuedArgSpecifications; 21 | } 22 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/AnyArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class AnyArgumentMatcher(Type typeArgMustBeCompatibleWith) : IArgumentMatcher 4 | { 5 | public override string ToString() => "any " + typeArgMustBeCompatibleWith.GetNonMangledTypeName(); 6 | 7 | public bool IsSatisfiedBy(object? argument) => argument.IsCompatibleWith(typeArgMustBeCompatibleWith); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ArgumentFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class ArgumentFormatter : IArgumentFormatter 4 | { 5 | internal static IArgumentFormatter Default { get; } = new ArgumentFormatter(); 6 | 7 | public string Format(object? argument, bool highlight) 8 | { 9 | var formatted = Format(argument); 10 | return highlight ? "*" + formatted + "*" : formatted; 11 | } 12 | 13 | private string Format(object? arg) 14 | { 15 | return arg switch 16 | { 17 | null => "", 18 | string str => $"\"{str}\"", 19 | { } obj when HasDefaultToString(obj) => arg.GetType().GetNonMangledTypeName(), 20 | _ => arg.ToString() ?? string.Empty 21 | }; 22 | 23 | static bool HasDefaultToString(object obj) 24 | => obj.GetType().GetMethod(nameof(ToString), Type.EmptyTypes)!.DeclaringType == typeof(object); 25 | } 26 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ArgumentMatchInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public record ArgumentMatchInfo 4 | { 5 | private readonly object? _argument; 6 | private readonly IArgumentSpecification _specification; 7 | 8 | public int Index { get; } 9 | 10 | public ArgumentMatchInfo(int index, object? argument, IArgumentSpecification specification) 11 | { 12 | _argument = argument; 13 | _specification = specification; 14 | Index = index; 15 | } 16 | 17 | public bool IsMatch => _specification.IsSatisfiedBy(_argument); 18 | 19 | public string DescribeNonMatch() 20 | { 21 | var describeNonMatch = _specification.DescribeNonMatch(_argument); 22 | if (string.IsNullOrEmpty(describeNonMatch)) return string.Empty; 23 | var argIndexPrefix = "arg[" + Index + "]: "; 24 | return string.Format("{0}{1}", argIndexPrefix, describeNonMatch.Replace("\n", "\n".PadRight(argIndexPrefix.Length + 1))); 25 | } 26 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ArgumentSpecificationCompatibilityTester.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class ArgumentSpecificationCompatibilityTester(IDefaultChecker defaultChecker) : IArgumentSpecificationCompatibilityTester 4 | { 5 | public bool IsSpecificationCompatible(IArgumentSpecification specification, object? argumentValue, Type argumentType) 6 | { 7 | var typeArgSpecIsFor = specification.ForType; 8 | return AreTypesCompatible(argumentType, typeArgSpecIsFor) 9 | && IsProvidedArgumentTheOneWeWouldGetUsingAnArgSpecForThisType(argumentValue, typeArgSpecIsFor); 10 | } 11 | 12 | private bool IsProvidedArgumentTheOneWeWouldGetUsingAnArgSpecForThisType(object? argument, Type typeArgSpecIsFor) 13 | { 14 | return defaultChecker.IsDefault(argument, typeArgSpecIsFor); 15 | } 16 | 17 | private bool AreTypesCompatible(Type argumentType, Type typeArgSpecIsFor) 18 | { 19 | return argumentType.IsAssignableFrom(typeArgSpecIsFor) || 20 | (argumentType.IsByRef && !typeArgSpecIsFor.IsByRef && argumentType.IsAssignableFrom(typeArgSpecIsFor.MakeByRefType())); 21 | } 22 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/DefaultChecker.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class DefaultChecker(IDefaultForType defaultForType) : IDefaultChecker 4 | { 5 | public bool IsDefault(object? value, Type forType) 6 | { 7 | return EqualityComparer.Default.Equals(value, defaultForType.GetDefaultFor(forType)); 8 | } 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/EqualsArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class EqualsArgumentMatcher(object? value) : IArgumentMatcher 4 | { 5 | public override string ToString() => ArgumentFormatter.Default.Format(value, false); 6 | 7 | public bool IsSatisfiedBy(object? argument) => EqualityComparer.Default.Equals(value, argument); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ExpressionArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace NSubstitute.Core.Arguments; 4 | 5 | public class ExpressionArgumentMatcher(Expression> predicate) : IArgumentMatcher 6 | { 7 | private readonly string _predicateDescription = predicate.ToString(); 8 | private readonly Predicate _predicate = predicate.Compile(); 9 | 10 | public bool IsSatisfiedBy(object? argument) => _predicate((T?)argument); 11 | 12 | public override string ToString() => _predicateDescription; 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface IArgumentFormatter 4 | { 5 | string Format(object? arg, bool highlight); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | /// 4 | /// Provides a specification for arguments. 5 | /// Can implement to give descriptions when arguments do not match. 6 | /// Can implement to give descriptions of expected arguments (otherwise 7 | /// `ToString()` will be used for descriptions). 8 | /// 9 | public interface IArgumentMatcher 10 | { 11 | /// 12 | /// Checks whether the satisfies the condition of the matcher. 13 | /// If this throws an exception the argument will be treated as non-matching. 14 | /// 15 | bool IsSatisfiedBy(object? argument); 16 | } 17 | 18 | /// 19 | /// Provides a specification for arguments. 20 | /// Can implement to give descriptions when arguments do not match. 21 | /// Can implement to give descriptions of expected arguments (otherwise 22 | /// `ToString()` will be used for descriptions). 23 | /// 24 | /// Matches arguments of type or compatible type. 25 | public interface IArgumentMatcher 26 | { 27 | /// 28 | /// Checks whether the satisfies the condition of the matcher. 29 | /// If this throws an exception the argument will be treated as non-matching. 30 | /// 31 | bool IsSatisfiedBy(T? argument); 32 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface IArgumentSpecification 4 | { 5 | bool IsSatisfiedBy(object? argument); 6 | Type ForType { get; } 7 | IArgumentSpecification CreateCopyMatchingAnyArgOfType(Type requiredType); 8 | bool HasAction { get; } 9 | void RunAction(object? argument); 10 | string DescribeNonMatch(object? argument); 11 | string FormatArgument(object? argument); 12 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentSpecificationCompatibilityTester.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface IArgumentSpecificationCompatibilityTester 4 | { 5 | bool IsSpecificationCompatible(IArgumentSpecification specification, object? argumentValue, Type argumentType); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentSpecificationFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface IArgumentSpecificationFactory 4 | { 5 | IArgumentSpecification Create(object? argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IArgumentSpecificationsFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core.Arguments; 4 | 5 | public interface IArgumentSpecificationsFactory 6 | { 7 | IEnumerable Create(IList argumentSpecs, object?[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/IDefaultChecker.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface IDefaultChecker 4 | { 5 | bool IsDefault(object? value, Type forType); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecifications.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface ISuppliedArgumentSpecifications 4 | { 5 | bool AnyFor(object? argument, Type argumentType); 6 | bool IsNextFor(object? argument, Type argumentType); 7 | IArgumentSpecification Dequeue(); 8 | IEnumerable DequeueRemaining(); 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecificationsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public interface ISuppliedArgumentSpecificationsFactory 4 | { 5 | ISuppliedArgumentSpecifications Create(IEnumerable argumentSpecifications); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/SuppliedArgumentSpecifications.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class SuppliedArgumentSpecifications : ISuppliedArgumentSpecifications 4 | { 5 | private readonly IArgumentSpecificationCompatibilityTester _argSpecCompatibilityTester; 6 | private readonly Queue _queue; 7 | private IReadOnlyCollection AllSpecifications { get; } 8 | 9 | public SuppliedArgumentSpecifications(IArgumentSpecificationCompatibilityTester argSpecCompatibilityTester, IEnumerable argumentSpecifications) 10 | { 11 | _argSpecCompatibilityTester = argSpecCompatibilityTester; 12 | AllSpecifications = argumentSpecifications.ToArray(); 13 | _queue = new Queue(AllSpecifications); 14 | } 15 | 16 | 17 | public bool AnyFor(object? argument, Type argumentType) 18 | { 19 | return AllSpecifications.Any(x => _argSpecCompatibilityTester.IsSpecificationCompatible(x, argument, argumentType)); 20 | } 21 | 22 | public bool IsNextFor(object? argument, Type argumentType) 23 | { 24 | if (_queue.Count == 0) 25 | { 26 | return false; 27 | } 28 | 29 | var nextArgSpec = _queue.Peek(); 30 | return _argSpecCompatibilityTester.IsSpecificationCompatible(nextArgSpec, argument, argumentType); 31 | } 32 | 33 | public IArgumentSpecification Dequeue() => _queue.Dequeue(); 34 | 35 | public IEnumerable DequeueRemaining() 36 | { 37 | var result = _queue.ToArray(); 38 | _queue.Clear(); 39 | return result; 40 | } 41 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Arguments/SuppliedArgumentSpecificationsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Arguments; 2 | 3 | public class SuppliedArgumentSpecificationsFactory(IArgumentSpecificationCompatibilityTester argumentSpecificationCompatTester) : ISuppliedArgumentSpecificationsFactory 4 | { 5 | public ISuppliedArgumentSpecifications Create(IEnumerable argumentSpecifications) 6 | { 7 | return new SuppliedArgumentSpecifications(argumentSpecificationCompatTester, argumentSpecifications); 8 | } 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core.Arguments; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public class CallFactory : ICallFactory 7 | { 8 | public ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications, Func? baseMethod) 9 | { 10 | return new Call(methodInfo, arguments, target, argumentSpecifications, baseMethod); 11 | } 12 | 13 | public ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications) 14 | { 15 | return new Call(methodInfo, arguments, target, argumentSpecifications, baseMethod: null); 16 | } 17 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class CallFormatter : IMethodInfoFormatter 6 | { 7 | internal static IMethodInfoFormatter Default { get; } = new CallFormatter(); 8 | 9 | private readonly IEnumerable _methodInfoFormatters; 10 | 11 | public CallFormatter() 12 | { 13 | _methodInfoFormatters = new IMethodInfoFormatter[] { 14 | new PropertyCallFormatter(), 15 | new EventCallFormatter(EventCallFormatter.IsSubscription), 16 | new EventCallFormatter(EventCallFormatter.IsUnsubscription), 17 | new MethodFormatter() }; 18 | } 19 | 20 | public bool CanFormat(MethodInfo methodInfo) => true; 21 | 22 | public string Format(MethodInfo methodInfoOfCall, IEnumerable formattedArguments) 23 | { 24 | return _methodInfoFormatters 25 | .First(x => x.CanFormat(methodInfoOfCall)) 26 | .Format(methodInfoOfCall, formattedArguments); 27 | } 28 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallInfoFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class CallInfoFactory : ICallInfoFactory 4 | { 5 | public CallInfo Create(ICall call) 6 | { 7 | var arguments = GetArgumentsFromCall(call); 8 | var genericArguments = call.GetMethodInfo().GetGenericArguments(); 9 | return new CallInfo(arguments, genericArguments); 10 | } 11 | 12 | private static Argument[] GetArgumentsFromCall(ICall call) 13 | { 14 | var result = new Argument[call.GetOriginalArguments().Length]; 15 | 16 | for (var i = 0; i < result.Length; i++) 17 | { 18 | result[i] = new Argument(call, i); 19 | } 20 | 21 | return result; 22 | } 23 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallRouterFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Routing; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class CallRouterFactory(IThreadLocalContext threadLocalContext, IRouteFactory routeFactory) : ICallRouterFactory 6 | { 7 | public ICallRouter Create(ISubstituteState substituteState, bool canConfigureBaseCalls) 8 | { 9 | // Cache popular routes which are bound to the particular substitute state when it's possible. 10 | var factoryWithCachedRoutes = new RouteFactoryCacheWrapper(routeFactory); 11 | return new CallRouter(substituteState, threadLocalContext, factoryWithCachedRoutes, canConfigureBaseCalls); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallRouterResolver.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class CallRouterResolver : ICallRouterResolver 6 | { 7 | public ICallRouter ResolveFor(object substitute) 8 | { 9 | return substitute switch 10 | { 11 | null => throw new NullSubstituteReferenceException(), 12 | ICallRouterProvider provider => provider.GetCallRouter(), 13 | Delegate { Target: ICallRouterProvider provider } => provider.GetCallRouter(), 14 | _ => throw new NotASubstituteException() 15 | }; 16 | } 17 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallSpecAndTarget.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class CallSpecAndTarget(ICallSpecification callSpecification, object target) 4 | { 5 | public ICallSpecification CallSpecification { get; } = callSpecification; 6 | public object Target { get; } = target; 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CallSpecificationFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core.Arguments; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class CallSpecificationFactory(IArgumentSpecificationsFactory argumentSpecificationsFactory) : ICallSpecificationFactory 6 | { 7 | public ICallSpecification CreateFrom(ICall call, MatchArgs matchArgs) 8 | { 9 | var methodInfo = call.GetMethodInfo(); 10 | var argumentSpecs = call.GetArgumentSpecifications(); 11 | var arguments = call.GetOriginalArguments(); 12 | var parameterInfos = call.GetParameterInfos(); 13 | var argumentSpecificationsForCall = argumentSpecificationsFactory.Create(argumentSpecs, arguments, parameterInfos, methodInfo, matchArgs); 14 | return new CallSpecification(methodInfo, argumentSpecificationsForCall); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/ConfigureCall.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class ConfigureCall(ICallResults configuredResults, ICallActions callActions, IGetCallSpec getCallSpec) : IConfigureCall 6 | { 7 | public ConfiguredCall SetResultForLastCall(IReturn valueToReturn, MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo) 8 | { 9 | var spec = getCallSpec.FromPendingSpecification(matchArgs, pendingSpecInfo); 10 | CheckResultIsCompatibleWithCall(valueToReturn, spec); 11 | configuredResults.SetResult(spec, valueToReturn); 12 | return new ConfiguredCall(action => callActions.Add(spec, action)); 13 | } 14 | 15 | public void SetResultForCall(ICall call, IReturn valueToReturn, MatchArgs matchArgs) 16 | { 17 | var spec = getCallSpec.FromCall(call, matchArgs); 18 | CheckResultIsCompatibleWithCall(valueToReturn, spec); 19 | configuredResults.SetResult(spec, valueToReturn); 20 | } 21 | 22 | private static void CheckResultIsCompatibleWithCall(IReturn valueToReturn, ICallSpecification spec) 23 | { 24 | var requiredReturnType = spec.ReturnType(); 25 | if (!valueToReturn.CanBeAssignedTo(requiredReturnType)) 26 | { 27 | throw new CouldNotSetReturnDueToTypeMismatchException(valueToReturn.TypeOrNull(), spec.GetMethodInfo()); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ConfiguredCall.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class ConfiguredCall(Action> addAction) 4 | { 5 | 6 | /// 7 | /// Adds a callback to execute for matching calls. 8 | /// 9 | /// an action to call 10 | /// 11 | public ConfiguredCall AndDoes(Action action) 12 | { 13 | addAction(action); 14 | return this; 15 | } 16 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/CustomHandlers.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class CustomHandlers(ISubstituteState substituteState) : ICustomHandlers 4 | { 5 | private readonly List _handlers = []; 6 | 7 | public IReadOnlyCollection Handlers => _handlers; 8 | 9 | public void AddCustomHandlerFactory(CallHandlerFactory factory) 10 | { 11 | _handlers.Add(factory.Invoke(substituteState)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/DefaultForType.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class DefaultForType : IDefaultForType 6 | { 7 | private static readonly object BoxedBoolean = default(bool); 8 | private static readonly object BoxedInt = default(int); 9 | private static readonly object BoxedLong = default(long); 10 | private static readonly object BoxedDouble = default(double); 11 | 12 | public object? GetDefaultFor(Type type) 13 | { 14 | if (IsVoid(type)) 15 | { 16 | return null; 17 | } 18 | 19 | if (type.GetTypeInfo().IsValueType) 20 | { 21 | return DefaultInstanceOfValueType(type); 22 | } 23 | 24 | return null; 25 | } 26 | 27 | private bool IsVoid(Type returnType) => returnType == typeof(void); 28 | 29 | private object DefaultInstanceOfValueType(Type returnType) 30 | { 31 | // Performance optimization for the most popular types. 32 | if (returnType == typeof(bool)) 33 | { 34 | return BoxedBoolean; 35 | } 36 | if (returnType == typeof(int)) 37 | { 38 | return BoxedInt; 39 | } 40 | if (returnType == typeof(long)) 41 | { 42 | return BoxedLong; 43 | } 44 | if (returnType == typeof(double)) 45 | { 46 | return BoxedDouble; 47 | } 48 | 49 | return Activator.CreateInstance(returnType)!; 50 | } 51 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/DependencyInjection/NSubLifetime.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.DependencyInjection; 2 | 3 | public enum NSubLifetime 4 | { 5 | /// 6 | /// Value is created only once. 7 | /// 8 | Singleton, 9 | /// 10 | /// Value is created only once per scope. Allows to share the same instance across the objects in the same graph. 11 | /// If no explicit scope is created, an implicit scope is created per single resolve request. 12 | /// 13 | PerScope, 14 | /// 15 | /// New value is created for each time. 16 | /// 17 | Transient 18 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/EventCallFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class EventCallFormatter(Func> eventsToFormat) : IMethodInfoFormatter 6 | { 7 | public static readonly Func> IsSubscription = 8 | call => (eventInfo => eventInfo.GetAddMethod() == call); 9 | public static readonly Func> IsUnsubscription = 10 | call => (eventInfo => eventInfo.GetRemoveMethod() == call); 11 | private readonly string _eventOperator = eventsToFormat == IsSubscription ? "+=" : "-="; 12 | 13 | public bool CanFormat(MethodInfo methodInfo) 14 | { 15 | return methodInfo.DeclaringType!.GetEvents().Any(x => eventsToFormat(methodInfo)(x)); 16 | } 17 | 18 | public string Format(MethodInfo methodInfo, IEnumerable arguments) 19 | { 20 | var eventInfo = methodInfo.DeclaringType!.GetEvents().First(x => eventsToFormat(methodInfo)(x)); 21 | return Format(eventInfo, _eventOperator, arguments); 22 | } 23 | 24 | private string Format(EventInfo eventInfo, string eventOperator, IEnumerable arguments) => 25 | $"{eventInfo.Name} {eventOperator} {arguments.Join(", ")}"; 26 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/EventHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class EventHandlerRegistry : IEventHandlerRegistry 4 | { 5 | // Collection consideration - events are used very rarely, so it makes no sense to allocate concurrent collection. 6 | // Events are not expected to be configured/raised concurrently, so simple locking should be sufficient. 7 | // List lookup is O(n), but for really small size performance is comparable to dictionary. 8 | // Given that normally a few events are configured only, it should be totally fine. 9 | private readonly List>> _handlersForEvent = []; 10 | 11 | public void Add(string eventName, object handler) 12 | { 13 | lock (_handlersForEvent) 14 | { 15 | Handlers(eventName).Add(handler); 16 | } 17 | } 18 | 19 | public void Remove(string eventName, object handler) 20 | { 21 | lock (_handlersForEvent) 22 | { 23 | Handlers(eventName).Remove(handler); 24 | } 25 | } 26 | 27 | public IEnumerable GetHandlers(string eventName) 28 | { 29 | lock (_handlersForEvent) 30 | { 31 | // Make snapshot to make code thread-safe. 32 | return Handlers(eventName).ToArray(); 33 | } 34 | } 35 | 36 | private List Handlers(string eventName) 37 | { 38 | foreach (var pair in _handlersForEvent) 39 | { 40 | if (pair.Item1 == eventName) 41 | { 42 | return pair.Item2; 43 | } 44 | } 45 | 46 | var newPair = Tuple.Create(eventName, new List()); 47 | _handlersForEvent.Add(newPair); 48 | return newPair.Item2; 49 | } 50 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Events/EventHandlerWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core.Events; 2 | 3 | public class EventHandlerWrapper(object? sender, EventArgs? eventArgs) : RaiseEventWrapper where TEventArgs : EventArgs 4 | { 5 | private readonly object? _sender = sender; 6 | private readonly EventArgs? _eventArgs = eventArgs; 7 | protected override string RaiseMethodName => "Raise.EventWith"; 8 | 9 | public EventHandlerWrapper() : this(null, null) { } 10 | 11 | public EventHandlerWrapper(EventArgs? eventArgs) : this(null, eventArgs) { } 12 | 13 | 14 | public static implicit operator EventHandler?(EventHandlerWrapper wrapper) 15 | { 16 | RaiseEvent(wrapper); 17 | return null; 18 | } 19 | 20 | public static implicit operator EventHandler?(EventHandlerWrapper wrapper) 21 | { 22 | RaiseEvent(wrapper); 23 | return null; 24 | } 25 | 26 | protected override object[] WorkOutRequiredArguments(ICall call) 27 | { 28 | var sender = _sender ?? call.Target(); 29 | var eventArgs = _eventArgs ?? GetDefaultForEventArgType(typeof(TEventArgs)); 30 | return [sender, eventArgs]; 31 | } 32 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/GetCallSpec.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class GetCallSpec( 4 | ICallCollection receivedCalls, 5 | ICallSpecificationFactory callSpecificationFactory, 6 | ICallActions callActions) : IGetCallSpec 7 | { 8 | public ICallSpecification FromPendingSpecification(MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo) 9 | { 10 | return pendingSpecInfo.Handle( 11 | callSpec => FromExistingSpec(callSpec, matchArgs), 12 | lastCall => 13 | { 14 | receivedCalls.Delete(lastCall); 15 | return FromCall(lastCall, matchArgs); 16 | }); 17 | } 18 | 19 | public ICallSpecification FromCall(ICall call, MatchArgs matchArgs) 20 | { 21 | return callSpecificationFactory.CreateFrom(call, matchArgs); 22 | } 23 | 24 | public ICallSpecification FromExistingSpec(ICallSpecification spec, MatchArgs matchArgs) 25 | { 26 | return matchArgs == MatchArgs.AsSpecifiedInCall ? spec : UpdateCallSpecToMatchAnyArgs(spec); 27 | } 28 | 29 | private ICallSpecification UpdateCallSpecToMatchAnyArgs(ICallSpecification callSpecification) 30 | { 31 | var anyArgCallSpec = callSpecification.CreateCopyThatMatchesAnyArguments(); 32 | callActions.MoveActionsForSpecToNewSpec(callSpecification, anyArgCallSpec); 33 | return anyArgCallSpec; 34 | } 35 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IArgumentSpecificationDequeue.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core.Arguments; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public interface IArgumentSpecificationDequeue 6 | { 7 | IList DequeueAllArgumentSpecificationsForMethod(int parametersCount); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICall.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core.Arguments; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public interface ICall 7 | { 8 | Type GetReturnType(); 9 | MethodInfo GetMethodInfo(); 10 | object?[] GetArguments(); 11 | object?[] GetOriginalArguments(); 12 | object Target(); 13 | IParameterInfo[] GetParameterInfos(); 14 | IList GetArgumentSpecifications(); 15 | void AssignSequenceNumber(long number); 16 | long GetSequenceNumber(); 17 | bool CanCallBase { get; } 18 | Maybe TryCallBase(); 19 | } 20 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallActions.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallActions 4 | { 5 | void Add(ICallSpecification callSpecification, Action action); 6 | void Add(ICallSpecification callSpec); 7 | void InvokeMatchingActions(ICall callInfo); 8 | void MoveActionsForSpecToNewSpec(ICallSpecification oldCallSpecification, ICallSpecification newCallSpecification); 9 | void Clear(); 10 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallBaseConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallBaseConfiguration 4 | { 5 | /// 6 | /// Gets or sets whether base method should be called by default. 7 | /// 8 | bool CallBaseByDefault { get; set; } 9 | 10 | /// 11 | /// Specifies whether base method should be always ignored for the matching call. 12 | /// If method is both explicitly excluded and included, base method is _not_ called. 13 | /// 14 | void Exclude(ICallSpecification callSpecification); 15 | 16 | /// 17 | /// Specifies whether base method should be called for the matching call. 18 | /// If method is both explicitly excluded and included, base method is _not_ called. 19 | /// 20 | void Include(ICallSpecification callSpecification); 21 | 22 | /// 23 | /// Tests whether base method should be called for the call given the existing configuration. 24 | /// 25 | bool ShouldCallBase(ICall call); 26 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallCollection.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallCollection 4 | { 5 | void Add(ICall call); 6 | void Delete(ICall call); 7 | IEnumerable AllCalls(); 8 | void Clear(); 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core.Arguments; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public interface ICallFactory 7 | { 8 | ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications, Func? baseMethod); 9 | ICall Create(MethodInfo methodInfo, object?[] getterArgs, object target, IList getArgumentSpecifications); 10 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallHandler.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallHandler 4 | { 5 | RouteAction Handle(ICall call); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallInfoFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallInfoFactory 4 | { 5 | CallInfo Create(ICall call); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallResults.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallResults 4 | { 5 | void SetResult(ICallSpecification callSpecification, IReturn result); 6 | bool TryGetResult(ICall call, out object? result); 7 | void Clear(); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallRouter.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallRouter 4 | { 5 | /// 6 | /// Specifies whether base method should be called by default. 7 | /// 8 | /// 9 | /// This configuration is considered only when base method exists (e.g. you created a substitute for 10 | /// the AbstractType with method implementation). 11 | /// 12 | bool CallBaseByDefault { get; set; } 13 | ConfiguredCall LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo); 14 | object? Route(ICall call); 15 | IEnumerable ReceivedCalls(); 16 | void SetReturnForType(Type type, IReturn returnValue); 17 | void RegisterCustomCallHandlerFactory(CallHandlerFactory factory); 18 | void Clear(ClearOptions clear); 19 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallRouterFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallRouterFactory 4 | { 5 | ICallRouter Create(ISubstituteState substituteState, bool canConfigureBaseCalls); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallRouterProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallRouterProvider 4 | { 5 | ICallRouter GetCallRouter(); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallRouterResolver.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallRouterResolver 4 | { 5 | ICallRouter ResolveFor(object substitute); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core.Arguments; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public interface ICallSpecification 7 | { 8 | bool IsSatisfiedBy(ICall call); 9 | string Format(ICall call); 10 | ICallSpecification CreateCopyThatMatchesAnyArguments(); 11 | void InvokePerArgumentActions(CallInfo callInfo); 12 | IEnumerable NonMatchingArguments(ICall call); 13 | MethodInfo GetMethodInfo(); 14 | Type ReturnType(); 15 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICallSpecificationFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ICallSpecificationFactory 4 | { 5 | ICallSpecification CreateFrom(ICall call, MatchArgs matchArgs); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IConfigureCall.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IConfigureCall 4 | { 5 | ConfiguredCall SetResultForLastCall(IReturn valueToReturn, MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo); 6 | void SetResultForCall(ICall call, IReturn valueToReturn, MatchArgs matchArgs); 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ICustomHandlers.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | /// 4 | /// Factory method which creates from the . 5 | /// 6 | public delegate ICallHandler CallHandlerFactory(ISubstituteState substituteState); 7 | 8 | public interface ICustomHandlers 9 | { 10 | IReadOnlyCollection Handlers { get; } 11 | 12 | void AddCustomHandlerFactory(CallHandlerFactory factory); 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IDefaultForType.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IDefaultForType 4 | { 5 | object? GetDefaultFor(Type type); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IDescribeNonMatches.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | /// 4 | /// A type that can describe how an argument does not match a required condition. 5 | /// Use in conjunction with to provide information about 6 | /// non-matches. 7 | /// 8 | public interface IDescribeNonMatches 9 | { 10 | /// 11 | /// Describes how the does not match the condition specified by this class, or 12 | /// if a detailed description can not be provided for the argument. 13 | /// 14 | /// 15 | /// Description of the non-match, or if no description can be provided. 16 | string DescribeFor(object? argument); 17 | } 18 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/IDescribeSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | /// 4 | /// A type that can describe the required conditions to meet a specification. 5 | /// Use in conjunction with to provide information about 6 | /// what it requires to match an argument. 7 | /// 8 | public interface IDescribeSpecification 9 | { 10 | /// 11 | /// A concise description of the conditions required to match this specification, or 12 | /// if a detailed description can not be provided. 13 | /// 14 | /// Description of the specification, or if no description can be provided. 15 | string DescribeSpecification(); 16 | } 17 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/IEventHandlerRegistry.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IEventHandlerRegistry 4 | { 5 | void Add(string eventName, object handler); 6 | void Remove(string eventName, object handler); 7 | IEnumerable GetHandlers(string eventName); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IGetCallSpec.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IGetCallSpec 4 | { 5 | ICallSpecification FromPendingSpecification(MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo); 6 | ICallSpecification FromExistingSpec(ICallSpecification spec, MatchArgs matchArgs); 7 | ICallSpecification FromCall(ICall call, MatchArgs matchArgs); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IMethodInfoFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public interface IMethodInfoFormatter 6 | { 7 | bool CanFormat(MethodInfo methodInfo); 8 | string Format(MethodInfo methodInfo, IEnumerable formattedArguments); 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IParameterInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IParameterInfo 4 | { 5 | Type ParameterType { get; } 6 | bool IsParams { get; } 7 | bool IsOptional { get; } 8 | bool IsOut { get; } 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IPendingSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IPendingSpecification 4 | { 5 | bool HasPendingCallSpecInfo(); 6 | PendingSpecificationInfo? UseCallSpecInfo(); 7 | void SetCallSpecification(ICallSpecification callSpecification); 8 | void SetLastCall(ICall lastCall); 9 | void Clear(); 10 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IPropertyHelper.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IPropertyHelper 4 | { 5 | bool IsCallToSetAReadWriteProperty(ICall call); 6 | ICall CreateCallToPropertyGetterFromSetterCall(ICall callToSetter); 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IProxyFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IProxyFactory 4 | { 5 | object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, bool isPartial, object?[]? constructorArguments); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IQuery 4 | { 5 | void RegisterCall(ICall call); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IQueryResults.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IQueryResults 4 | { 5 | IEnumerable MatchingCallsInOrder(); 6 | IEnumerable QuerySpecification(); 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IReceivedCallsExceptionThrower.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.ReceivedExtensions; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public interface IReceivedCallsExceptionThrower 6 | { 7 | void Throw(ICallSpecification callSpecification, IEnumerable matchingCalls, IEnumerable nonMatchingCalls, Quantity requiredQuantity); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/IResultsForType.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface IResultsForType 4 | { 5 | void SetResult(Type type, IReturn resultToReturn); 6 | bool TryGetResult(ICall call, out object? result); 7 | void Clear(); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ISubstituteFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ISubstituteFactory 4 | { 5 | object Create(Type[] typesToProxy, object[] constructorArguments); 6 | object CreatePartial(Type[] typesToProxy, object[] constructorArguments); 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ISubstituteState.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Routing.AutoValues; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public interface ISubstituteState 6 | { 7 | ICallBaseConfiguration CallBaseConfiguration { get; } 8 | ICallCollection ReceivedCalls { get; } 9 | ICallResults CallResults { get; } 10 | ICallActions CallActions { get; } 11 | IConfigureCall ConfigureCall { get; } 12 | IEventHandlerRegistry EventHandlerRegistry { get; } 13 | IReadOnlyCollection AutoValueProviders { get; } 14 | ICallResults AutoValuesCallResults { get; } 15 | IResultsForType ResultsForType { get; } 16 | ICustomHandlers CustomHandlers { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/ISubstituteStateFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public interface ISubstituteStateFactory 4 | { 5 | ISubstituteState Create(ISubstituteFactory substituteFactory); 6 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ISubstitutionContext.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Routing; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public interface ISubstitutionContext 6 | { 7 | ISubstituteFactory SubstituteFactory { get; } 8 | IRouteFactory RouteFactory { get; } 9 | ICallSpecificationFactory CallSpecificationFactory { get; } 10 | 11 | /// 12 | /// A thread bound state of the NSubstitute context. Usually this API is used to provide the fluent 13 | /// features of the NSubstitute. 14 | /// 15 | IThreadLocalContext ThreadContext { get; } 16 | 17 | ICallRouter GetCallRouterFor(object substitute); 18 | } 19 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/MatchArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | [DebuggerDisplay("{" + nameof(_name) + "}")] 6 | public class MatchArgs 7 | { 8 | private readonly string _name; 9 | 10 | public static readonly MatchArgs AsSpecifiedInCall = new(nameof(AsSpecifiedInCall)); 11 | public static readonly MatchArgs Any = new(nameof(Any)); 12 | 13 | private MatchArgs(string name) 14 | { 15 | _name = name; 16 | } 17 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Maybe.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | /// 6 | /// Particularly poor implementation of Maybe/Option type. 7 | /// This is just filling an immediate need; use FSharpOption or XSharpx or similar for a 8 | /// real implementation. 9 | /// 10 | /// 11 | public struct Maybe : IEnumerable 12 | { 13 | private readonly bool hasValue; 14 | private readonly T value; 15 | 16 | public Maybe(T value) : this() 17 | { 18 | this.value = value; 19 | hasValue = true; 20 | } 21 | 22 | public bool HasValue() { return hasValue; } 23 | 24 | public Maybe OrElse(Func> other) { var current = this; return Fold(other, _ => current); } 25 | public Maybe OrElse(Maybe other) { return OrElse(() => other); } 26 | public T ValueOr(Func other) { return Fold(other, x => x); } 27 | public T ValueOr(T other) { return ValueOr(() => other); } 28 | public T? ValueOrDefault() { return ValueOr(default(T)!); } 29 | 30 | public TResult Fold(Func handleNoValue, Func handleValue) 31 | { 32 | return HasValue() ? handleValue(value) : handleNoValue(); 33 | } 34 | 35 | IEnumerator IEnumerable.GetEnumerator() 36 | { 37 | if (hasValue) 38 | { 39 | yield return value; 40 | } 41 | } 42 | 43 | IEnumerator IEnumerable.GetEnumerator() 44 | { 45 | return ((IEnumerable)this).GetEnumerator(); 46 | } 47 | } 48 | 49 | public static class Maybe 50 | { 51 | public static Maybe Just(T value) { return new Maybe(value); } 52 | public static Maybe Nothing() { return new Maybe(); } 53 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/MethodFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class MethodFormatter : IMethodInfoFormatter 6 | { 7 | public bool CanFormat(MethodInfo methodInfo) 8 | { 9 | return true; 10 | } 11 | 12 | public string Format(MethodInfo methodInfo, IEnumerable arguments) 13 | { 14 | var args = string.Join(", ", arguments); 15 | return $"{methodInfo.Name}{FormatGenericType(methodInfo)}({args})"; 16 | } 17 | 18 | private static string FormatGenericType(MethodInfo methodInfoOfCall) 19 | { 20 | if (!methodInfoOfCall.IsGenericMethod) 21 | { 22 | return string.Empty; 23 | } 24 | 25 | var genericArgs = methodInfoOfCall.GetGenericArguments(); 26 | return $"<{string.Join(", ", genericArgs.Select(x => x.GetNonMangledTypeName()))}>"; 27 | } 28 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ParameterInfoWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | internal class ParameterInfoWrapper(ParameterInfo parameterInfo) : IParameterInfo 6 | { 7 | public Type ParameterType => parameterInfo.ParameterType; 8 | 9 | public bool IsParams => parameterInfo.IsParams(); 10 | 11 | public bool IsOptional => parameterInfo.IsOptional; 12 | 13 | public bool IsOut => parameterInfo.IsOut; 14 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/PendingSpecificationInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class PendingSpecificationInfo 4 | { 5 | private readonly ICallSpecification? _callSpecification; 6 | private readonly ICall? _lastCall; 7 | 8 | private PendingSpecificationInfo(ICallSpecification? callSpecification, ICall? lastCall) 9 | { 10 | _callSpecification = callSpecification; 11 | _lastCall = lastCall; 12 | } 13 | 14 | public T Handle(Func onCallSpec, Func onLastCall) 15 | { 16 | return _callSpecification != null ? onCallSpec(_callSpecification) : onLastCall(_lastCall!); 17 | } 18 | 19 | public static PendingSpecificationInfo FromLastCall(ICall lastCall) 20 | { 21 | return new PendingSpecificationInfo(null, lastCall); 22 | } 23 | 24 | public static PendingSpecificationInfo FromCallSpecification(ICallSpecification callSpecification) 25 | { 26 | return new PendingSpecificationInfo(callSpecification, null); 27 | } 28 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/Query.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class Query(ICallSpecificationFactory callSpecificationFactory) : IQuery, IQueryResults 4 | { 5 | private readonly List _querySpec = []; 6 | private readonly HashSet _matchingCalls = new(new CallSequenceNumberComparer()); 7 | 8 | public void RegisterCall(ICall call) 9 | { 10 | var target = call.Target(); 11 | var callSpecification = callSpecificationFactory.CreateFrom(call, MatchArgs.AsSpecifiedInCall); 12 | 13 | _querySpec.Add(new CallSpecAndTarget(callSpecification, target)); 14 | 15 | var allMatchingCallsOnTarget = target.ReceivedCalls().Where(callSpecification.IsSatisfiedBy); 16 | _matchingCalls.UnionWith(allMatchingCallsOnTarget); 17 | } 18 | 19 | public IQueryResults Result() => this; 20 | 21 | IEnumerable IQueryResults.MatchingCallsInOrder() => _matchingCalls.OrderBy(x => x.GetSequenceNumber()); 22 | 23 | IEnumerable IQueryResults.QuerySpecification() => _querySpec.Select(x => x); 24 | 25 | private class CallSequenceNumberComparer : IEqualityComparer 26 | { 27 | public bool Equals(ICall? x, ICall? y) => x?.GetSequenceNumber() == y?.GetSequenceNumber(); 28 | 29 | public int GetHashCode(ICall obj) => obj.GetSequenceNumber().GetHashCode(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ResultsForType.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core.Arguments; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public class ResultsForType(ICallInfoFactory callInfoFactory) : IResultsForType 7 | { 8 | private readonly CallResults _results = new CallResults(callInfoFactory); 9 | 10 | public void SetResult(Type type, IReturn resultToReturn) 11 | { 12 | _results.SetResult(new MatchingReturnTypeSpecification(type), resultToReturn); 13 | } 14 | 15 | public bool TryGetResult(ICall call, out object? result) 16 | { 17 | return _results.TryGetResult(call, out result); 18 | } 19 | 20 | public void Clear() 21 | { 22 | _results.Clear(); 23 | } 24 | 25 | private class MatchingReturnTypeSpecification(Type expectedReturnType) : ICallSpecification 26 | { 27 | public bool IsSatisfiedBy(ICall call) 28 | => call.GetReturnType() == expectedReturnType; 29 | 30 | // ******* Rest methods are not required ******* 31 | 32 | public string Format(ICall call) 33 | => throw new NotSupportedException(); 34 | 35 | public ICallSpecification CreateCopyThatMatchesAnyArguments() 36 | => throw new NotSupportedException(); 37 | 38 | public void InvokePerArgumentActions(CallInfo callInfo) 39 | => throw new NotSupportedException(); 40 | 41 | public IEnumerable NonMatchingArguments(ICall call) 42 | => throw new NotSupportedException(); 43 | 44 | public MethodInfo GetMethodInfo() 45 | => throw new NotSupportedException(); 46 | 47 | public Type ReturnType() 48 | => throw new NotSupportedException(); 49 | } 50 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/ReturnObservable.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | internal class ReturnObservable(T? value) : IObservable 4 | { 5 | public ReturnObservable() : this(default) { } 6 | 7 | public IDisposable Subscribe(IObserver observer) 8 | { 9 | observer.OnNext(value); 10 | observer.OnCompleted(); 11 | 12 | return EmptyDisposable.Instance; 13 | } 14 | } 15 | 16 | internal class EmptyDisposable : IDisposable 17 | { 18 | public static IDisposable Instance { get; } = new EmptyDisposable(); 19 | 20 | public void Dispose() { } 21 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/RobustThreadLocal.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | /// 4 | /// Delegates to ThreadLocal<T>, but wraps Value property access in try/catch to swallow ObjectDisposedExceptions. 5 | /// These can occur if the Value property is accessed from the finalizer thread. Because we can't detect this, we'll 6 | /// just swallow the exception (the finalizer thread won't be using any of the values from thread local storage anyway). 7 | /// 8 | internal class RobustThreadLocal 9 | { 10 | private readonly ThreadLocal _threadLocal; 11 | private readonly Func? _initialValueFactory; 12 | 13 | public RobustThreadLocal() 14 | { 15 | _threadLocal = new ThreadLocal(); 16 | } 17 | 18 | public RobustThreadLocal(Func initialValueFactory) 19 | { 20 | _initialValueFactory = initialValueFactory; 21 | _threadLocal = new ThreadLocal(initialValueFactory); 22 | } 23 | 24 | public T Value 25 | { 26 | get 27 | { 28 | // Suppress nullability for result, as we trust type by usage. 29 | // For non-nullable we expect ctor with default to be used. 30 | try 31 | { 32 | return _threadLocal.Value!; 33 | } 34 | catch (ObjectDisposedException) 35 | { 36 | return _initialValueFactory != null ? _initialValueFactory.Invoke() : default!; 37 | } 38 | } 39 | set 40 | { 41 | try 42 | { 43 | _threadLocal.Value = value; 44 | } 45 | catch (ObjectDisposedException) 46 | { 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/RouteAction.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class RouteAction 4 | { 5 | private static readonly RouteAction _continue = new(hasReturnValue: false, null); 6 | 7 | public bool HasReturnValue { get; } 8 | 9 | public object? ReturnValue { get; } 10 | 11 | public static RouteAction Continue() => _continue; 12 | public static RouteAction Return(object? value) => new(hasReturnValue: true, value); 13 | 14 | private RouteAction(bool hasReturnValue, object? returnValue) 15 | { 16 | HasReturnValue = hasReturnValue; 17 | ReturnValue = returnValue; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/SequenceChecking/InstanceTracker.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace NSubstitute.Core.SequenceChecking; 4 | 5 | public class InstanceTracker 6 | { 7 | private readonly Dictionary _instances = new(new ReferenceEqualityComparer()); 8 | private int _counter = 0; 9 | 10 | public int InstanceNumber(object o) 11 | { 12 | if (_instances.TryGetValue(o, out var i)) 13 | { 14 | return i; 15 | } 16 | 17 | var next = ++_counter; 18 | _instances.Add(o, next); 19 | return next; 20 | } 21 | 22 | public int NumberOfInstances() => _counter; 23 | 24 | private class ReferenceEqualityComparer : IEqualityComparer 25 | { 26 | public new bool Equals(object? x, object? y) => ReferenceEquals(x, y); 27 | public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); 28 | } 29 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/SequenceNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Core; 2 | 3 | public class SequenceNumberGenerator 4 | { 5 | private long _current = long.MinValue; 6 | 7 | public virtual long Next() 8 | { 9 | return Interlocked.Increment(ref _current); 10 | } 11 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/SubstituteState.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Routing.AutoValues; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class SubstituteState : ISubstituteState 6 | { 7 | public ICallBaseConfiguration CallBaseConfiguration { get; } 8 | public ICallCollection ReceivedCalls { get; } 9 | public ICallResults CallResults { get; } 10 | public ICallActions CallActions { get; } 11 | public IConfigureCall ConfigureCall { get; } 12 | public IEventHandlerRegistry EventHandlerRegistry { get; } 13 | public IReadOnlyCollection AutoValueProviders { get; } 14 | public ICallResults AutoValuesCallResults { get; } 15 | public IResultsForType ResultsForType { get; } 16 | public ICustomHandlers CustomHandlers { get; } 17 | 18 | public SubstituteState(ICallSpecificationFactory callSpecificationFactory, 19 | ICallInfoFactory callInfoFactory, 20 | IReadOnlyCollection autoValueProviders) 21 | { 22 | AutoValueProviders = autoValueProviders; 23 | 24 | var callCollection = new CallCollection(); 25 | ReceivedCalls = callCollection; 26 | 27 | CallBaseConfiguration = new CallBaseConfiguration(); 28 | CallResults = new CallResults(callInfoFactory); 29 | AutoValuesCallResults = new CallResults(callInfoFactory); 30 | CallActions = new CallActions(callInfoFactory); 31 | ResultsForType = new ResultsForType(callInfoFactory); 32 | CustomHandlers = new CustomHandlers(this); 33 | var getCallSpec = new GetCallSpec(callCollection, callSpecificationFactory, CallActions); 34 | ConfigureCall = new ConfigureCall(CallResults, CallActions, getCallSpec); 35 | EventHandlerRegistry = new EventHandlerRegistry(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/NSubstitute/Core/SubstituteStateFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Routing.AutoValues; 2 | 3 | namespace NSubstitute.Core; 4 | 5 | public class SubstituteStateFactory(ICallSpecificationFactory callSpecificationFactory, 6 | ICallInfoFactory callInfoFactory, 7 | IAutoValueProvidersFactory autoValueProvidersFactory) : ISubstituteStateFactory 8 | { 9 | public ISubstituteState Create(ISubstituteFactory substituteFactory) 10 | { 11 | var autoValueProviders = autoValueProvidersFactory.CreateProviders(substituteFactory); 12 | return new SubstituteState(callSpecificationFactory, callInfoFactory, autoValueProviders); 13 | } 14 | } -------------------------------------------------------------------------------- /src/NSubstitute/Core/SubstitutionContext.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core.DependencyInjection; 2 | using NSubstitute.Routing; 3 | 4 | namespace NSubstitute.Core; 5 | 6 | public class SubstitutionContext( 7 | ISubstituteFactory substituteFactory, 8 | IRouteFactory routeFactory, 9 | ICallSpecificationFactory callSpecificationFactory, 10 | IThreadLocalContext threadLocalContext, 11 | ICallRouterResolver callRouterResolver) : ISubstitutionContext 12 | { 13 | public static ISubstitutionContext Current { get; set; } 14 | 15 | public ISubstituteFactory SubstituteFactory { get; } = substituteFactory; 16 | public IRouteFactory RouteFactory { get; } = routeFactory; 17 | public IThreadLocalContext ThreadContext { get; } = threadLocalContext; 18 | public ICallSpecificationFactory CallSpecificationFactory { get; } = callSpecificationFactory; 19 | 20 | static SubstitutionContext() 21 | { 22 | Current = NSubstituteDefaultFactory.CreateSubstitutionContext(); 23 | } 24 | 25 | public ICallRouter GetCallRouterFor(object substitute) => 26 | callRouterResolver.ResolveFor(substitute); 27 | } 28 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ArgumentIsNotOutOrRefException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ArgumentIsNotOutOrRefException(int argumentIndex, Type argumentType) : SubstituteException(string.Format(WhatProbablyWentWrong, argumentIndex, argumentType.Name)) 4 | { 5 | private const string WhatProbablyWentWrong = "Could not set argument {0} ({1}) as it is not an out or ref argument."; 6 | } 7 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ArgumentNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ArgumentNotFoundException(string message) : SubstituteException(message) 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ArgumentSetWithIncompatibleValueException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ArgumentSetWithIncompatibleValueException(int argumentIndex, Type argumentType, Type typeOfValueWeTriedToAssign) : SubstituteException(string.Format(WhatProbablyWentWrong, argumentIndex, argumentType.Name, typeOfValueWeTriedToAssign.Name)) 4 | { 5 | private const string WhatProbablyWentWrong = 6 | "Could not set value of type {2} to argument {0} ({1}) because the types are incompatible."; 7 | } 8 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CallSequenceNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CallSequenceNotFoundException(string message) : SubstituteException(message) 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CanNotPartiallySubForInterfaceOrDelegateException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CanNotPartiallySubForInterfaceOrDelegateException(Type type) : SubstituteException(DescribeProblem(type)) 4 | { 5 | private static string DescribeProblem(Type type) 6 | { 7 | return string.Format("Can only substitute for parts of classes, not interfaces or delegates. " 8 | + "Try `Substitute.For<{0}> instead.", type.Name); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CannotCreateEventArgsException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CannotCreateEventArgsException : SubstituteException 4 | { 5 | public CannotCreateEventArgsException() { } 6 | public CannotCreateEventArgsException(string message) : base(message) { } 7 | public CannotCreateEventArgsException(string message, Exception innerException) : base(message, innerException) { } 8 | } 9 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CannotReturnNullforValueType.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CannotReturnNullForValueType(Type valueType) : SubstituteException(string.Format(Description, valueType.Name)) 4 | { 5 | private const string Description = 6 | "Cannot return null for {0} because it is a value type. " + 7 | "If you want to return the default value for this type use \"default({0})\"."; 8 | } 9 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CouldNotConfigureBaseMethodException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CouldNotConfigureCallBaseException(string message) : SubstituteException(message) 4 | { 5 | private const string CannotConfigureSingleCallMessage = 6 | "Cannot configure the base method call as base method implementation is missing. " + 7 | "You can call base method only if you create a class substitute and the method is not abstract."; 8 | 9 | private const string CannotConfigureAllCallsMessage = 10 | "Base method calls can be configured for a class substitute only, " + 11 | "as otherwise base implementation does not exist."; 12 | 13 | internal static CouldNotConfigureCallBaseException ForSingleCall() => 14 | new CouldNotConfigureCallBaseException(CannotConfigureSingleCallMessage); 15 | 16 | internal static CouldNotConfigureCallBaseException ForAllCalls() => 17 | new CouldNotConfigureCallBaseException(CannotConfigureAllCallsMessage); 18 | } -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/CouldNotRaiseEventException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class CouldNotRaiseEventException : SubstituteException 4 | { 5 | protected const string WhatProbablyWentWrong = 6 | "Make sure you are using Raise.Event() as part of an event subscription on a substitute.\n" + 7 | "For example:\n" + 8 | "\tmySub.Clicked += Raise.Event();\n" + 9 | "\n" + 10 | "If you substituted for a class rather than an interface, check that the event on your substitute is virtual/abstract.\n" + 11 | "Events on classes cannot be raised if they are not declared virtual or abstract.\n" + 12 | "\n" + 13 | "Note that the source of the problem may be prior to where this exception was thrown (possibly in a previous test!).\n" + 14 | "For example:\n" + 15 | "\tvar notASub = new Button();\n" + 16 | "\tnotASub.Clicked += Raise.Event(); // <-- Problem here. This is not a substitute.\n" + 17 | "\tvar sub = Substitute.For();\n" + 18 | "\tsub.Load(); // <-- Exception thrown here. NSubstitute thinks the earlier Raise.Event() was meant for this call."; 19 | 20 | public CouldNotRaiseEventException() : base(WhatProbablyWentWrong) { } 21 | } 22 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/MissingSequenceNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class MissingSequenceNumberException : SubstituteException 4 | { 5 | public MissingSequenceNumberException() { } 6 | } 7 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/NotASubstituteException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class NotASubstituteException : SubstituteException 4 | { 5 | private const string Explanation = 6 | "NSubstitute extension methods like .Received() can only be called " + 7 | "on objects created using Substitute.For() and related methods."; 8 | 9 | public NotASubstituteException() : base(Explanation) { } 10 | } 11 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/NotRunningAQueryException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class NotRunningAQueryException : SubstituteException 4 | { 5 | public NotRunningAQueryException() { } 6 | } 7 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/NullSubstituteReferenceException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class NullSubstituteReferenceException : SubstituteException 4 | { 5 | private const string Explanation = "NSubstitute extension methods like .Received() can only be called on non-null objects."; 6 | 7 | public NullSubstituteReferenceException() : base(Explanation) { } 8 | } 9 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ProtectedMethodNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ProtectedMethodNotFoundException(string message, Exception? innerException) : SubstituteException(message, innerException) 4 | { 5 | public ProtectedMethodNotFoundException() : this("", null) 6 | { } 7 | 8 | public ProtectedMethodNotFoundException(string message) : this(message, null) 9 | { } 10 | } -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ProtectedMethodNotVirtualException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ProtectedMethodNotVirtualException(string message, Exception? innerException) : SubstituteException(message, innerException) 4 | { 5 | public ProtectedMethodNotVirtualException() : this("", null) 6 | { } 7 | 8 | public ProtectedMethodNotVirtualException(string message) : this(message, null) 9 | { } 10 | } -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/ReceivedCallsException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class ReceivedCallsException : SubstituteException 4 | { 5 | public ReceivedCallsException() { } 6 | public ReceivedCallsException(string message) : base(message) { } 7 | public ReceivedCallsException(string message, Exception innerException) : base(message, innerException) { } 8 | } 9 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/SubstituteException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class SubstituteException(string message, Exception? innerException) : Exception(message, innerException) 4 | { 5 | public SubstituteException() : this("") { } 6 | public SubstituteException(string message) : this(message, null) { } 7 | } 8 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/SubstituteInternalException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | 4 | public class SubstituteInternalException(string message, Exception? innerException) : SubstituteException("Please report this exception at https://github.com/nsubstitute/NSubstitute/issues: \n\n" + message, 5 | innerException) 6 | { 7 | public SubstituteInternalException() : this("") { } 8 | public SubstituteInternalException(string message) : this(message, null) { } 9 | } -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/TypeForwardingException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public abstract class TypeForwardingException(string message) : SubstituteException(message) 4 | { 5 | } 6 | 7 | public sealed class CanNotForwardCallsToClassNotImplementingInterfaceException(Type type) : TypeForwardingException(DescribeProblem(type)) 8 | { 9 | private static string DescribeProblem(Type type) 10 | { 11 | return string.Format("The provided class '{0}' doesn't implement all requested interfaces. ", type.Name); 12 | } 13 | } 14 | 15 | public sealed class CanNotForwardCallsToAbstractClassException(Type type) : TypeForwardingException(DescribeProblem(type)) 16 | { 17 | private static string DescribeProblem(Type type) 18 | { 19 | return string.Format("The provided class '{0}' is abstract. ", type.Name); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/NSubstitute/Exceptions/UnexpectedArgumentMatcherException.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Exceptions; 2 | 3 | public class UnexpectedArgumentMatcherException(string message) : SubstituteException(message) 4 | { 5 | public static readonly string WhatProbablyWentWrong = 6 | "Argument matchers (Arg.Is, Arg.Any) should only be used in place of member arguments. " + 7 | "Do not use in a Returns() statement or anywhere else outside of a member call." + Environment.NewLine + 8 | "Correct use:" + Environment.NewLine + 9 | " sub.MyMethod(Arg.Any()).Returns(\"hi\")" + Environment.NewLine + 10 | "Incorrect use:" + Environment.NewLine + 11 | " sub.MyMethod(\"hi\").Returns(Arg.Any())"; 12 | public UnexpectedArgumentMatcherException() : this(WhatProbablyWentWrong) { } 13 | } 14 | -------------------------------------------------------------------------------- /src/NSubstitute/Extensions/ClearExtensions.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.ClearExtensions; 5 | 6 | public static class ClearExtensions 7 | { 8 | /// 9 | /// Clears received calls, configured return values and/or call actions for this substitute. 10 | /// 11 | /// 12 | /// 13 | /// Specifies what to clear on the substitute. Can be combined with | to 14 | /// clear multiple aspects at once. 15 | /// 16 | /// 17 | public static void ClearSubstitute(this T substitute, ClearOptions options = ClearOptions.All) where T : class 18 | { 19 | if (substitute == null) throw new NullSubstituteReferenceException(); 20 | 21 | var context = SubstitutionContext.Current; 22 | var router = context.GetCallRouterFor(substitute!); 23 | router.Clear(options); 24 | } 25 | } -------------------------------------------------------------------------------- /src/NSubstitute/Extensions/ConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.Extensions; 5 | 6 | public static class ConfigurationExtensions 7 | { 8 | /// 9 | /// A hint for the NSubstitute that the subsequent method/property call is about to be configured. 10 | /// For example: substitute.Configure().GetValue().Returns(1,2,3); 11 | /// 12 | /// NOTICE, you _don't need_ to invoke this method for the basic configuration scenarios. 13 | /// Ensure you don't overuse this method and it is applied only if strictly required. 14 | /// 15 | /// 16 | /// Due to the NSubstitute configuration syntax it is often impossible to recognise during the method call 17 | /// dispatch whether this is a setup phase or a regular method call. 18 | /// Usually it doesn't matter, however sometimes method invocation could lead to undesired side effects 19 | /// (e.g. the previously configured value is returned, base method is invoked). In that case you might want to 20 | /// provide NSubstitute with a hint that you are configuring a method, so it handles the call in configuration mode. 21 | /// 22 | /// 23 | public static T Configure(this T substitute) where T : class 24 | { 25 | if (substitute == null) throw new NullSubstituteReferenceException(); 26 | 27 | var context = SubstitutionContext.Current; 28 | var callRouter = context.GetCallRouterFor(substitute!); 29 | context.ThreadContext.SetNextRoute(callRouter, context.RouteFactory.RecordCallSpecification); 30 | 31 | return substitute; 32 | } 33 | } -------------------------------------------------------------------------------- /src/NSubstitute/Extensions/ReturnsForAllExtensions.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.Extensions; 5 | 6 | public static class ReturnsForAllExtensions 7 | { 8 | /// 9 | /// Configure default return value for all methods that return the specified type 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static void ReturnsForAll(this object substitute, T returnThis) 16 | { 17 | if (substitute == null) throw new NullSubstituteReferenceException(); 18 | 19 | var callRouter = SubstitutionContext.Current.GetCallRouterFor(substitute); 20 | callRouter.SetReturnForType(typeof(T), new ReturnValue(returnThis)); 21 | } 22 | 23 | /// 24 | /// Configure default return value for all methods that return the specified type, calculated by a function 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static void ReturnsForAll(this object substitute, Func returnThis) 31 | { 32 | if (substitute == null) throw new NullSubstituteReferenceException(); 33 | 34 | var callRouter = SubstitutionContext.Current.GetCallRouterFor(substitute); 35 | callRouter.SetReturnForType(typeof(T), new ReturnValueFromFunc(returnThis)); 36 | } 37 | } -------------------------------------------------------------------------------- /src/NSubstitute/Proxies/CastleDynamicProxy/CastleForwardingInterceptor.cs: -------------------------------------------------------------------------------- 1 | using Castle.DynamicProxy; 2 | using NSubstitute.Core; 3 | 4 | namespace NSubstitute.Proxies.CastleDynamicProxy; 5 | 6 | public class CastleForwardingInterceptor(CastleInvocationMapper invocationMapper, ICallRouter callRouter) : IInterceptor 7 | { 8 | private bool _fullDispatchMode; 9 | 10 | public void Intercept(IInvocation invocation) 11 | { 12 | ICall mappedInvocation = invocationMapper.Map(invocation); 13 | 14 | if (_fullDispatchMode) 15 | { 16 | invocation.ReturnValue = callRouter.Route(mappedInvocation); 17 | return; 18 | } 19 | 20 | // Fallback to the base value until the full dispatch mode is activated. 21 | // Useful to ensure that object is initialized properly. 22 | if (callRouter.CallBaseByDefault) 23 | { 24 | invocation.ReturnValue = mappedInvocation.TryCallBase().ValueOrDefault(); 25 | } 26 | } 27 | 28 | /// 29 | /// Switches interceptor to dispatch calls via the full pipeline. 30 | /// 31 | public void SwitchToFullDispatchMode() 32 | { 33 | _fullDispatchMode = true; 34 | } 35 | } -------------------------------------------------------------------------------- /src/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs: -------------------------------------------------------------------------------- 1 | using Castle.DynamicProxy; 2 | using NSubstitute.Core; 3 | 4 | namespace NSubstitute.Proxies.CastleDynamicProxy; 5 | 6 | public class CastleInvocationMapper(ICallFactory callFactory, IArgumentSpecificationDequeue argSpecificationDequeue) 7 | { 8 | public virtual ICall Map(IInvocation castleInvocation) 9 | { 10 | Func? baseMethod = null; 11 | if (castleInvocation.InvocationTarget != null && 12 | castleInvocation.MethodInvocationTarget.IsVirtual && 13 | !castleInvocation.MethodInvocationTarget.IsAbstract) 14 | { 15 | baseMethod = CreateBaseResultInvocation(castleInvocation); 16 | } 17 | 18 | var queuedArgSpecifications = argSpecificationDequeue.DequeueAllArgumentSpecificationsForMethod(castleInvocation.Arguments.Length); 19 | return callFactory.Create(castleInvocation.Method, castleInvocation.Arguments, castleInvocation.Proxy, queuedArgSpecifications, baseMethod); 20 | } 21 | 22 | private static Func CreateBaseResultInvocation(IInvocation invocation) 23 | { 24 | // Notice, it's important to keep this as a separate method, as methods with lambda closures 25 | // always allocate, even if delegate is not actually constructed. 26 | // This way we make allocation only if indeed required. 27 | Func baseResult = () => { invocation.Proceed(); return invocation.ReturnValue; }; 28 | var result = new Lazy(baseResult); 29 | return () => result.Value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/NSubstitute/Proxies/CastleDynamicProxy/ProxyIdInterceptor.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Reflection; 3 | using Castle.DynamicProxy; 4 | using NSubstitute.Core; 5 | 6 | namespace NSubstitute.Proxies.CastleDynamicProxy; 7 | 8 | public class ProxyIdInterceptor(Type primaryProxyType) : IInterceptor 9 | { 10 | private string? _cachedProxyId; 11 | 12 | public void Intercept(IInvocation invocation) 13 | { 14 | if (IsDefaultToStringMethod(invocation.Method)) 15 | { 16 | invocation.ReturnValue = _cachedProxyId ??= GenerateId(invocation); 17 | return; 18 | } 19 | 20 | invocation.Proceed(); 21 | } 22 | 23 | private string GenerateId(IInvocation invocation) 24 | { 25 | var proxy = invocation.InvocationTarget; 26 | 27 | var shortTypeName = primaryProxyType.GetNonMangledTypeName(); 28 | var proxyHashCode = proxy.GetHashCode(); 29 | 30 | return string.Format(CultureInfo.InvariantCulture, "Substitute.{0}|{1:x8}", shortTypeName, proxyHashCode); 31 | } 32 | 33 | public static bool IsDefaultToStringMethod(MethodInfo methodInfo) 34 | { 35 | return methodInfo.DeclaringType == typeof(object) 36 | && string.Equals(methodInfo.Name, nameof(ToString), StringComparison.Ordinal) 37 | && methodInfo.GetParameters().Length == 0; 38 | } 39 | } -------------------------------------------------------------------------------- /src/NSubstitute/Received.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Core.SequenceChecking; 3 | 4 | namespace NSubstitute; 5 | 6 | public class Received 7 | { 8 | /// 9 | /// Asserts the calls to the substitutes contained in the given Action were 10 | /// received by these substitutes in the same order. Calls to property getters are not included 11 | /// in the assertion. 12 | /// 13 | /// Action containing calls to substitutes in the expected order 14 | public static void InOrder(Action calls) 15 | { 16 | var query = new Query(SubstitutionContext.Current.CallSpecificationFactory); 17 | SubstitutionContext.Current.ThreadContext.RunInQueryContext(calls, query); 18 | new SequenceInOrderAssertion().Assert(query.Result()); 19 | } 20 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoArrayProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Routing.AutoValues; 2 | 3 | public class AutoArrayProvider : IAutoValueProvider 4 | { 5 | public bool CanProvideValueFor(Type type) => 6 | type.IsArray; 7 | 8 | public object GetValue(Type type) 9 | { 10 | var rank = type.GetArrayRank(); 11 | var dimensionLengths = new int[rank]; 12 | return Array.CreateInstance(type.GetElementType()!, dimensionLengths); 13 | } 14 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoObservableProvider.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using System.Reflection; 3 | 4 | namespace NSubstitute.Routing.AutoValues; 5 | 6 | public class AutoObservableProvider(Lazy> autoValueProviders) : IAutoValueProvider 7 | { 8 | public bool CanProvideValueFor(Type type) => 9 | type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IObservable<>); 10 | 11 | public object? GetValue(Type type) 12 | { 13 | if (!CanProvideValueFor(type)) 14 | throw new InvalidOperationException(); 15 | 16 | Type innerType = type.GetGenericArguments()[0]; 17 | var valueProvider = autoValueProviders.Value.FirstOrDefault(vp => vp.CanProvideValueFor(innerType)); 18 | var value = valueProvider == null ? GetDefault(type) : valueProvider.GetValue(innerType); 19 | return Activator.CreateInstance(typeof(ReturnObservable<>).MakeGenericType(innerType), [value]); 20 | } 21 | 22 | private static object? GetDefault(Type type) 23 | { 24 | return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoQueryableProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Routing.AutoValues; 4 | 5 | public class AutoQueryableProvider : IAutoValueProvider 6 | { 7 | public bool CanProvideValueFor(Type type) => 8 | type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IQueryable<>); 9 | 10 | public object GetValue(Type type) 11 | { 12 | if (!CanProvideValueFor(type)) 13 | throw new InvalidOperationException(); 14 | 15 | Type innerType = type.GetGenericArguments()[0]; 16 | 17 | return Array.CreateInstance(innerType, 0).AsQueryable(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoStringProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Routing.AutoValues; 2 | 3 | public class AutoStringProvider : IAutoValueProvider 4 | { 5 | public bool CanProvideValueFor(Type type) => type == typeof(string); 6 | 7 | public object GetValue(Type type) 8 | { 9 | return string.Empty; 10 | } 11 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoTaskProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace NSubstitute.Routing.AutoValues; 4 | 5 | public class AutoTaskProvider(Lazy> autoValueProviders) : IAutoValueProvider 6 | { 7 | public bool CanProvideValueFor(Type type) => typeof(Task).IsAssignableFrom(type); 8 | 9 | public object GetValue(Type type) 10 | { 11 | if (!CanProvideValueFor(type)) 12 | throw new InvalidOperationException(); 13 | 14 | if (type.GetTypeInfo().IsGenericType) 15 | { 16 | var taskType = type.GetGenericArguments()[0]; 17 | var valueProvider = autoValueProviders.Value.FirstOrDefault(vp => vp.CanProvideValueFor(taskType)); 18 | 19 | var value = valueProvider == null ? GetDefault(type) : valueProvider.GetValue(taskType); 20 | var taskCompletionSourceType = typeof(TaskCompletionSource<>).MakeGenericType(taskType); 21 | var taskCompletionSource = Activator.CreateInstance(taskCompletionSourceType); 22 | taskCompletionSourceType.GetMethod(nameof(TaskCompletionSource.SetResult))!.Invoke(taskCompletionSource, [value]); 23 | return taskCompletionSourceType.GetProperty(nameof(TaskCompletionSource.Task))!.GetValue(taskCompletionSource, null)!; 24 | } 25 | else 26 | { 27 | var taskCompletionSource = new TaskCompletionSource(); 28 | taskCompletionSource.SetResult(null); 29 | return taskCompletionSource.Task; 30 | } 31 | } 32 | 33 | private static object? GetDefault(Type type) 34 | { 35 | return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/AutoValueProvidersFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.Routing.AutoValues; 5 | 6 | public class AutoValueProvidersFactory : IAutoValueProvidersFactory 7 | { 8 | public IReadOnlyCollection CreateProviders(ISubstituteFactory substituteFactory) 9 | { 10 | IAutoValueProvider[]? result = null; 11 | var lazyResult = new Lazy>( 12 | () => result ?? throw new SubstituteInternalException("Value was not constructed yet."), 13 | LazyThreadSafetyMode.PublicationOnly); 14 | 15 | result = 16 | [ 17 | new AutoObservableProvider(lazyResult), 18 | new AutoQueryableProvider(), 19 | new AutoSubstituteProvider(substituteFactory), 20 | new AutoStringProvider(), 21 | new AutoArrayProvider(), 22 | new AutoTaskProvider(lazyResult) 23 | ]; 24 | 25 | return result; 26 | } 27 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/IAutoValueProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Routing.AutoValues; 2 | 3 | public interface IAutoValueProvider 4 | { 5 | bool CanProvideValueFor(Type type); 6 | object? GetValue(Type type); 7 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/AutoValues/IAutoValueProvidersFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.AutoValues; 4 | 5 | public interface IAutoValueProvidersFactory 6 | { 7 | IReadOnlyCollection CreateProviders(ISubstituteFactory substituteFactory); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/AddCallToQueryResultHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class AddCallToQueryResultHandler(IThreadLocalContext threadContext) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | threadContext.RegisterInContextQuery(call); 10 | 11 | return RouteAction.Continue(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/CallBaseForCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.Routing.Handlers; 5 | 6 | public class CallBaseForCallHandler(ICallSpecificationFactory callSpecificationFactory, ICallBaseConfiguration callBaseConfig, MatchArgs matchArgs) : ICallHandler 7 | { 8 | public RouteAction Handle(ICall call) 9 | { 10 | if (!call.CanCallBase) throw CouldNotConfigureCallBaseException.ForSingleCall(); 11 | 12 | var callSpec = callSpecificationFactory.CreateFrom(call, matchArgs); 13 | callBaseConfig.Include(callSpec); 14 | 15 | return RouteAction.Continue(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/CheckReceivedCallsHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.ReceivedExtensions; 3 | 4 | namespace NSubstitute.Routing.Handlers; 5 | 6 | public class CheckReceivedCallsHandler(ICallCollection receivedCalls, ICallSpecificationFactory callSpecificationFactory, IReceivedCallsExceptionThrower exceptionThrower, MatchArgs matchArgs, Quantity requiredQuantity) : ICallHandler 7 | { 8 | public RouteAction Handle(ICall call) 9 | { 10 | var callSpecification = callSpecificationFactory.CreateFrom(call, matchArgs); 11 | var allCallsToMethodSpec = callSpecificationFactory.CreateFrom(call, MatchArgs.Any); 12 | 13 | var allCalls = receivedCalls.AllCalls().ToList(); 14 | var matchingCalls = allCalls.Where(callSpecification.IsSatisfiedBy).ToList(); 15 | 16 | if (!requiredQuantity.Matches(matchingCalls)) 17 | { 18 | var relatedCalls = allCalls.Where(allCallsToMethodSpec.IsSatisfiedBy).Except(matchingCalls); 19 | exceptionThrower.Throw(callSpecification, matchingCalls, relatedCalls, requiredQuantity); 20 | } 21 | 22 | return RouteAction.Continue(); 23 | } 24 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ClearLastCallRouterHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | /// 6 | /// Clears last call router on SubstitutionContext for routes that do not require it. 7 | /// 8 | /// 9 | /// This is to help prevent static state bleeding over into future calls. 10 | /// 11 | public class ClearLastCallRouterHandler(IThreadLocalContext threadContext) : ICallHandler 12 | { 13 | public RouteAction Handle(ICall call) 14 | { 15 | threadContext.ClearLastCallRouter(); 16 | 17 | return RouteAction.Continue(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ClearUnusedCallSpecHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ClearUnusedCallSpecHandler(IPendingSpecification pendingSpecification) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | pendingSpecification.Clear(); 10 | 11 | return RouteAction.Continue(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/DoActionsCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class DoActionsCallHandler(ICallActions callActions) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | callActions.InvokeMatchingActions(call); 10 | 11 | return RouteAction.Continue(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/DoNotCallBaseForCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | 4 | namespace NSubstitute.Routing.Handlers; 5 | 6 | public class DoNotCallBaseForCallHandler(ICallSpecificationFactory callSpecificationFactory, ICallBaseConfiguration callBaseConfig, MatchArgs matchArgs) : ICallHandler 7 | { 8 | public RouteAction Handle(ICall call) 9 | { 10 | if (!call.CanCallBase) throw CouldNotConfigureCallBaseException.ForSingleCall(); 11 | 12 | var callSpec = callSpecificationFactory.CreateFrom(call, matchArgs); 13 | callBaseConfig.Exclude(callSpec); 14 | 15 | return RouteAction.Continue(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/PropertySetterHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class PropertySetterHandler(IPropertyHelper propertyHelper, IConfigureCall configureCall) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | if (propertyHelper.IsCallToSetAReadWriteProperty(call)) 10 | { 11 | var callToPropertyGetter = propertyHelper.CreateCallToPropertyGetterFromSetterCall(call); 12 | // It's important to use original arguments, as it provides better performance. 13 | // It's safe to use original arguments here, as only by-ref arguments might be modified, 14 | // which should never happen for this case. 15 | var valueBeingSetOnProperty = call.GetOriginalArguments().Last(); 16 | configureCall.SetResultForCall(callToPropertyGetter, new ReturnValue(valueBeingSetOnProperty), MatchArgs.AsSpecifiedInCall); 17 | } 18 | 19 | return RouteAction.Continue(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/RaiseEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NSubstitute.Core; 3 | using NSubstitute.Exceptions; 4 | 5 | namespace NSubstitute.Routing.Handlers; 6 | 7 | public class RaiseEventHandler(IEventHandlerRegistry eventHandlerRegistry, Func getEventArguments) : ICallHandler 8 | { 9 | public RouteAction Handle(ICall call) 10 | { 11 | var methodInfo = call.GetMethodInfo(); 12 | var eventInfo = FindEventInfo(methodInfo); 13 | if (eventInfo == null) 14 | { 15 | throw new CouldNotRaiseEventException(); 16 | } 17 | 18 | object?[] eventArguments = getEventArguments(call); 19 | var handlers = eventHandlerRegistry.GetHandlers(eventInfo.Name); 20 | foreach (Delegate handler in handlers) 21 | { 22 | if (handler == null) 23 | { 24 | continue; 25 | } 26 | 27 | try 28 | { 29 | (handler.DynamicInvoke(eventArguments) as Task)?.GetAwaiter().GetResult(); 30 | } 31 | catch (TargetInvocationException e) 32 | { 33 | throw e.InnerException!; 34 | } 35 | } 36 | 37 | return RouteAction.Continue(); 38 | 39 | static EventInfo? FindEventInfo(MethodInfo mi) 40 | { 41 | return mi.DeclaringType!.GetEvents().FirstOrDefault( 42 | e => e.GetAddMethod() == mi || e.GetRemoveMethod() == mi); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/RecordCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class RecordCallHandler(ICallCollection callCollection, SequenceNumberGenerator generator) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | call.AssignSequenceNumber(generator.Next()); 10 | callCollection.Add(call); 11 | 12 | return RouteAction.Continue(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/RecordCallSpecificationHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class RecordCallSpecificationHandler(IPendingSpecification pendingCallSpecification, ICallSpecificationFactory callSpecificationFactory, ICallActions callActions) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | var callSpec = callSpecificationFactory.CreateFrom(call, MatchArgs.AsSpecifiedInCall); 10 | pendingCallSpecification.SetCallSpecification(callSpec); 11 | 12 | // Performance optimization - don't register call actions if current argument matchers 13 | // don't have any callbacks. 14 | if (call.GetArgumentSpecifications().Any(x => x.HasAction)) 15 | { 16 | callActions.Add(callSpec); 17 | } 18 | 19 | return RouteAction.Continue(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ReturnConfiguredResultHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ReturnConfiguredResultHandler(ICallResults callResults) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | if (callResults.TryGetResult(call, out var configuredResult)) 10 | { 11 | return RouteAction.Return(configuredResult); 12 | } 13 | 14 | return RouteAction.Continue(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ReturnDefaultForReturnTypeHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ReturnDefaultForReturnTypeHandler(IDefaultForType defaultForType) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | var returnValue = defaultForType.GetDefaultFor(call.GetMethodInfo().ReturnType); 10 | return RouteAction.Return(returnValue); 11 | } 12 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ReturnFromBaseIfRequired.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ReturnFromBaseIfRequired(ICallBaseConfiguration config) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | if (config.ShouldCallBase(call)) 10 | { 11 | return call 12 | .TryCallBase() 13 | .Fold(RouteAction.Continue, RouteAction.Return); 14 | } 15 | 16 | return RouteAction.Continue(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ReturnFromCustomHandlers.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ReturnFromCustomHandlers(ICustomHandlers customHandlers) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | // Performance optimization, as enumerator retrieval allocates. 10 | if (customHandlers.Handlers.Count == 0) 11 | { 12 | return RouteAction.Continue(); 13 | } 14 | 15 | foreach (var handler in customHandlers.Handlers) 16 | { 17 | var result = handler.Handle(call); 18 | if (result.HasReturnValue) 19 | { 20 | return result; 21 | } 22 | } 23 | 24 | return RouteAction.Continue(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/ReturnResultForTypeHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class ReturnResultForTypeHandler(IResultsForType resultsForType) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | if (resultsForType.TryGetResult(call, out var result)) 10 | { 11 | return RouteAction.Return(result); 12 | } 13 | 14 | return RouteAction.Continue(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/SetActionForCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class SetActionForCallHandler( 6 | ICallSpecificationFactory callSpecificationFactory, 7 | ICallActions callActions, 8 | Action action, 9 | MatchArgs matchArgs) : ICallHandler 10 | { 11 | public RouteAction Handle(ICall call) 12 | { 13 | var callSpec = callSpecificationFactory.CreateFrom(call, matchArgs); 14 | callActions.Add(callSpec, action); 15 | 16 | return RouteAction.Continue(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Handlers/TrackLastCallHandler.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing.Handlers; 4 | 5 | public class TrackLastCallHandler(IPendingSpecification pendingSpecification) : ICallHandler 6 | { 7 | public RouteAction Handle(ICall call) 8 | { 9 | pendingSpecification.SetLastCall(call); 10 | 11 | return RouteAction.Continue(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/IRoute.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing; 4 | 5 | public interface IRoute 6 | { 7 | object? Handle(ICall call); 8 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/IRouteFactory.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.ReceivedExtensions; 3 | 4 | namespace NSubstitute.Routing; 5 | 6 | public interface IRouteFactory 7 | { 8 | IRoute CallQuery(ISubstituteState state); 9 | IRoute CheckReceivedCalls(ISubstituteState state, MatchArgs matchArgs, Quantity requiredQuantity); 10 | IRoute DoWhenCalled(ISubstituteState state, Action doAction, MatchArgs matchArgs); 11 | IRoute DoNotCallBase(ISubstituteState state, MatchArgs matchArgs); 12 | IRoute CallBase(ISubstituteState state, MatchArgs matchArgs); 13 | IRoute RaiseEvent(ISubstituteState state, Func getEventArguments); 14 | IRoute RecordCallSpecification(ISubstituteState state); 15 | IRoute RecordReplay(ISubstituteState state); 16 | } -------------------------------------------------------------------------------- /src/NSubstitute/Routing/Route.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute.Routing; 4 | 5 | public class Route(ICallHandler[] handlers) : IRoute 6 | { 7 | public IEnumerable Handlers => handlers; 8 | 9 | public object? Handle(ICall call) 10 | { 11 | // This is a hot method which is invoked frequently and has major impact on performance. 12 | // Therefore, the LINQ cycle was unwinded to for loop. 13 | for (int i = 0; i < handlers.Length; i++) 14 | { 15 | var result = handlers[i].Handle(call); 16 | if (result.HasReturnValue) 17 | { 18 | return result.ReturnValue; 19 | } 20 | } 21 | 22 | return null; 23 | } 24 | } -------------------------------------------------------------------------------- /src/NSubstitute/SubstituteExtensions.When.Task.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute; 4 | 5 | public static partial class SubstituteExtensions 6 | { 7 | /// 8 | /// Perform an action when this member is called. 9 | /// Must be followed by to provide the callback. 10 | /// 11 | public static WhenCalled When(this T substitute, Func substituteCall) where T : class 12 | { 13 | return MakeWhenCalled(substitute, x => substituteCall(x), MatchArgs.AsSpecifiedInCall); 14 | } 15 | 16 | /// 17 | /// Perform an action when this member is called with any arguments. 18 | /// Must be followed by to provide the callback. 19 | /// 20 | public static WhenCalled WhenForAnyArgs(this T substitute, Func substituteCall) where T : class 21 | { 22 | return MakeWhenCalled(substitute, x => substituteCall(x), MatchArgs.Any); 23 | } 24 | } -------------------------------------------------------------------------------- /src/NSubstitute/SubstituteExtensions.When.ValueTask.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | 3 | namespace NSubstitute; 4 | 5 | public static partial class SubstituteExtensions 6 | { 7 | /// 8 | /// Perform an action when this member is called. 9 | /// Must be followed by to provide the callback. 10 | /// 11 | public static WhenCalled When(this TSubstitute substitute, 12 | Func> substituteCall) where TSubstitute : class 13 | { 14 | return MakeWhenCalled(substitute, x => substituteCall(x), MatchArgs.AsSpecifiedInCall); 15 | } 16 | 17 | /// 18 | /// Perform an action when this member is called with any arguments. 19 | /// Must be followed by to provide the callback. 20 | /// 21 | public static WhenCalled WhenForAnyArgs(this TSubstitute substitute, 22 | Func> substituteCall) where TSubstitute : class 23 | { 24 | return MakeWhenCalled(substitute, x => substituteCall(x), MatchArgs.Any); 25 | } 26 | } -------------------------------------------------------------------------------- /src/NSubstitute/SubstituteExtensions.When.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Core; 2 | using NSubstitute.Exceptions; 3 | namespace NSubstitute; 4 | 5 | public static partial class SubstituteExtensions 6 | { 7 | /// 8 | /// Perform an action when this member is called. 9 | /// Must be followed by to provide the callback. 10 | /// 11 | public static WhenCalled When(this T substitute, Action substituteCall) where T : class 12 | { 13 | return MakeWhenCalled(substitute, substituteCall, MatchArgs.AsSpecifiedInCall); 14 | } 15 | 16 | /// 17 | /// Perform an action when this member is called with any arguments. 18 | /// Must be followed by to provide the callback. 19 | /// 20 | public static WhenCalled WhenForAnyArgs(this T substitute, Action substituteCall) where T : class 21 | { 22 | return MakeWhenCalled(substitute, substituteCall, MatchArgs.Any); 23 | } 24 | 25 | private static WhenCalled MakeWhenCalled(TSubstitute? substitute, 26 | Action action, MatchArgs matchArgs) 27 | { 28 | if (substitute == null) throw new NullSubstituteReferenceException(); 29 | 30 | var context = SubstitutionContext.Current; 31 | return new WhenCalled(context, substitute, action, matchArgs); 32 | } 33 | } -------------------------------------------------------------------------------- /src/NSubstitute/nsubstitute.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsubstitute/NSubstitute/af8df59f2df7435d613e840f600750814370567d/src/NSubstitute/nsubstitute.snk -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/AssemblySigningTest.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs; 5 | 6 | public class AssemblySigningTest 7 | { 8 | [Test] 9 | public void NSubstitute_assembly_should_be_signed() 10 | { 11 | var assemblyName = typeof(Substitute).GetTypeInfo().Assembly.GetName(); 12 | var publicKeyToken = string.Join("", assemblyName.GetPublicKeyToken().Select(x => x.ToString("x"))); 13 | 14 | Assert.That(publicKeyToken, Is.EqualTo("92dd2e9066daa5ca")); 15 | } 16 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/AsyncEventHandlers/ITestInterface.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Acceptance.Specs.AsyncEventHandlers; 2 | 3 | public delegate Task TestEventHandler(); 4 | 5 | public interface ITestInterface 6 | { 7 | #region Events 8 | 9 | event TestEventHandler TestEvent; 10 | 11 | #endregion 12 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/AsyncEventHandlers/TestImplementation.cs: -------------------------------------------------------------------------------- 1 | namespace NSubstitute.Acceptance.Specs.AsyncEventHandlers; 2 | 3 | public class TestImplementation : ITestInterface 4 | { 5 | #region Events 6 | 7 | public event TestEventHandler? TestEvent; 8 | 9 | #endregion 10 | 11 | #region Public Methods 12 | 13 | public async Task RaiseEventAsync() 14 | { 15 | if (TestEvent != null) 16 | { 17 | await TestEvent(); 18 | } 19 | } 20 | 21 | #endregion 22 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/ArgMatchingWithNestedSubCalls.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class ArgMatchingWithNestedSubCalls 6 | { 7 | public interface IHaveAMethod { void Method(int a, string b); } 8 | public interface IStealArgMatchers 9 | { 10 | string StealMatcherBeforeUsedElsewhere { get; } 11 | string this[int i] { get; } 12 | } 13 | 14 | [Test] 15 | public void Use_arg_matcher_then_access_another_sub_without_args_before_call_spec_is_created() 16 | { 17 | var sub = Substitute.For(); 18 | var stealer = Substitute.For(); 19 | 20 | sub.Method(2, stealer.StealMatcherBeforeUsedElsewhere); 21 | 22 | sub.Received().Method(Arg.Any(), stealer.StealMatcherBeforeUsedElsewhere); 23 | } 24 | 25 | [Test] 26 | [Pending, Explicit] 27 | public void Use_arg_matcher_then_access_another_sub_with_args_before_call_spec_is_created() 28 | { 29 | var sub = Substitute.For(); 30 | var stealer = Substitute.For(); 31 | stealer[0].Returns("a"); 32 | 33 | sub.Method(2, stealer[0]); 34 | 35 | //This example still blows up because the call to stealer[0] takes the Arg.Any() matcher 36 | //away. The call router thinks the Arg.Any belongs to that call, not the sub.Method() call. 37 | sub.Received().Method(Arg.Any(), stealer[0]); 38 | } 39 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/DisposeWithThreadLocal.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class DisposeWithThreadLocal 6 | { 7 | [Test] 8 | public void DisposeSubstituteAndPerformGC() 9 | { 10 | using (var s = Substitute.For()) { } 11 | GC.Collect(); 12 | GC.WaitForPendingFinalizers(); 13 | 14 | //Exception thrown on background thread. Can view this from output of test runner. 15 | } 16 | 17 | public class DisposableClass : IDisposable 18 | { 19 | bool disposed = false; 20 | ~DisposableClass() { Dispose(false); } 21 | public virtual void Dispose() { Dispose(true); } 22 | protected virtual void Dispose(bool disposing) 23 | { 24 | if (!disposed) { if (disposing) { disposed = true; } } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/EqualsBehaviourOnClassSubs.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class EqualsBehaviourOnClassSubs 7 | { 8 | [Test] 9 | public void Equals_should_work_as_expected_for_class_substitutes() 10 | { 11 | var firstSub = Substitute.For(); 12 | var secondSub = Substitute.For(); 13 | 14 | ClassicAssert.AreEqual(firstSub, firstSub); 15 | ClassicAssert.AreNotEqual(firstSub, secondSub); 16 | } 17 | 18 | public class AClass { } 19 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue110_CustomExceptions.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue110_CustomExceptions 6 | { 7 | public class MyException : Exception { } 8 | 9 | public interface IThrow { void Throw(); } 10 | public interface IDoStuff { event Action StuffDone; } 11 | 12 | [Test] 13 | public void ThrowExceptionWithoutSerialisationConstructor() 14 | { 15 | var ithrow = Substitute.For(); 16 | var doStuff = Substitute.For(); 17 | 18 | ithrow.When(x => x.Throw()).Do(x => { throw new MyException(); }); 19 | doStuff.StuffDone += ithrow.Throw; 20 | 21 | Assert.Throws(() => doStuff.StuffDone += Raise.Event()); 22 | } 23 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue125_MethodWithSealedClassReturnType.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue125_MethodWithSealedClassReturnType 6 | { 7 | public sealed class SealedClass { } 8 | 9 | public interface IInterface 10 | { 11 | SealedClass MethodWithSealedClassReturnType(); 12 | } 13 | 14 | [Test] 15 | public void MethodWithSealedClassReturnTypeReturnsCorrectResult() 16 | { 17 | var substitute = Substitute.For(); 18 | var expected = new SealedClass(); 19 | substitute.MethodWithSealedClassReturnType().Returns(expected); 20 | 21 | var result = substitute.MethodWithSealedClassReturnType(); 22 | 23 | Assert.That(result, Is.EqualTo(expected)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue149_ArgMatcherInReturns.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue149_ArgMatcherInReturns 7 | { 8 | public interface ISub1 { Item GetItem(string s); } 9 | public interface ISub2 { string GetSignature(int i); } 10 | public class Item { } 11 | 12 | [Test] 13 | public void MatcherInReturnsShouldThrow() 14 | { 15 | var sub2 = Substitute.For(); 16 | Assert.Throws( 17 | () => sub2.GetSignature(1).Returns(Arg.Any())); 18 | } 19 | 20 | // Original example from https://github.com/nsubstitute/NSubstitute/issues/149 21 | [Test] 22 | public void OriginalMatcherInReturnsExample() 23 | { 24 | var sub1 = Substitute.For(); 25 | var sub2 = Substitute.For(); 26 | sub1.GetItem(Arg.Any()).Returns(new Item()); 27 | 28 | Assert.Throws(() => 29 | sub2.GetSignature(1).Returns(Arg.Any()) // <-- THIS IS THE PROBLEM 30 | ); 31 | 32 | sub1.GetItem("mystring"); 33 | 34 | sub1.ReceivedWithAnyArgs(1).GetItem("mystring"); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue170_MultidimensionalArray.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue170_MultidimensionalArray 6 | { 7 | public interface ITest 8 | { 9 | bool[,] Method(); 10 | } 11 | 12 | [Test] 13 | public void Method_Works() 14 | { 15 | var test = Substitute.For(); 16 | test.Method(); 17 | } 18 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue225_ConfiguredValueIsUsedInSubsequentSetups.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Acceptance.Specs.Infrastructure; 2 | using NUnit.Framework; 3 | using NUnit.Framework.Legacy; 4 | 5 | namespace NSubstitute.Acceptance.Specs.FieldReports; 6 | 7 | public class Issue225_ConfiguredValueIsUsedInSubsequentSetups 8 | { 9 | [Test] 10 | public void ShouldNotUseTheConfiguredValueDuringSubsequentSetup() 11 | { 12 | // Arrange 13 | var target = Substitute.For(); 14 | 15 | // Act 16 | target.Echo(Arg.Is(0)).Returns("00", "01", "02"); 17 | target.Echo(Arg.Is(1)).Returns("10", "11", "12"); 18 | 19 | // Assert 20 | ClassicAssert.AreEqual("00", target.Echo(0)); 21 | ClassicAssert.AreEqual("10", target.Echo(1)); 22 | ClassicAssert.AreEqual("01", target.Echo(0)); 23 | ClassicAssert.AreEqual("11", target.Echo(1)); 24 | ClassicAssert.AreEqual("02", target.Echo(0)); 25 | ClassicAssert.AreEqual("12", target.Echo(1)); 26 | } 27 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue271_DelegateOutArgument.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue271_DelegateOutArgument 7 | { 8 | public delegate void Foo(out int bar); 9 | 10 | [Test] 11 | public void DelegateReturnsOutParameter() 12 | { 13 | var foo = Substitute.For(); 14 | int bar; 15 | foo.When(x => x(out bar)).Do(x => { x[0] = 42; }); 16 | 17 | foo(out bar); 18 | 19 | ClassicAssert.AreEqual(42, bar); 20 | } 21 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue279_ShouldFailOnRedundantArguments.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue279_ShouldFailOnRedundantArguments 7 | { 8 | public interface IFoo 9 | { 10 | int Blah(double s); 11 | } 12 | 13 | [Test] 14 | public void Should_fail_with_redundant_exception_if_matcher_is_not_used_due_to_implicit_cast_scenario_1() 15 | { 16 | var foo = Substitute.For(); 17 | 18 | Assert.Throws(() => 19 | { 20 | foo.Blah(Arg.Any()).Returns(42); 21 | }); 22 | } 23 | 24 | [Test] 25 | public void Should_fail_with_redundant_exception_if_matcher_is_not_used_due_to_implicit_cast_scenario_2() 26 | { 27 | var foo = Substitute.For(); 28 | 29 | Assert.Throws(() => 30 | { 31 | // Fails because Is() type is deduced to int, so specifier is not matched later. 32 | foo.Blah(Arg.Is(10)).Returns(42); 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue282_MultipleReturnValuesParallelism.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | [TestFixture] 7 | public class Issue282_MultipleReturnValuesParallelism 8 | { 9 | public interface IFoo 10 | { 11 | string Foo(); 12 | } 13 | 14 | [Test] 15 | public void ReturnsMultipleValuesInParallel() 16 | { 17 | var ret1 = "One"; 18 | var ret2 = "Two"; 19 | 20 | var substitute = Substitute.For(); 21 | substitute.Foo().Returns(ret1, ret2); 22 | 23 | var runningTask1 = Task.Run(() => substitute.Foo()); 24 | var runningTask2 = Task.Run(() => substitute.Foo()); 25 | 26 | var results = Task.WhenAll(runningTask1, runningTask2).Result; 27 | 28 | ClassicAssert.Contains(ret1, results); 29 | ClassicAssert.Contains(ret2, results); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue33_RaisingINotifyPropertyChangedEvents.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue33_RaisingINotifyPropertyChangedEvents 7 | { 8 | [Test] 9 | public void Should_be_able_to_raise_event() 10 | { 11 | var sub = Substitute.For(); 12 | bool wasCalled = false; 13 | sub.PropertyChanged += (sender, args) => wasCalled = true; 14 | 15 | sub.PropertyChanged += Raise.Event(this, new PropertyChangedEventArgs("test")); 16 | 17 | Assert.That(wasCalled); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue372_InterfaceSameNameOfMethods.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue372_InterfaceSameNameOfMethods 7 | { 8 | public interface A 9 | { 10 | } 11 | 12 | public interface B 13 | { 14 | } 15 | 16 | public interface X 17 | { 18 | Task Foo(B bar); 19 | Task Foo(A bar); 20 | } 21 | 22 | [Test] 23 | public void Should_create_substitute() 24 | { 25 | var sut = Substitute.For(); 26 | ClassicAssert.NotNull(sut); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue38_SettingNullReturnValue.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue38_SettingNullReturnValue 7 | { 8 | public interface IDoSomething 9 | { 10 | object Something(); 11 | int SomethingWithValueType(); 12 | } 13 | 14 | [Test] 15 | public void CanSetCallToReturnNull() 16 | { 17 | var doSomething = Substitute.For(); 18 | doSomething.Something().Returns(null); 19 | var result = doSomething.Something(); 20 | Assert.That(result, Is.Null); 21 | } 22 | 23 | [Test] 24 | public void SettingCallWhichReturnsAValueTypeToNullShouldThrow() 25 | { 26 | var doSomething = Substitute.For(); 27 | Assert.That(() => doSomething.SomethingWithValueType().Returns(null), Throws.TypeOf()); 28 | } 29 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue45_CallInfoArgAccessFailsForNull.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue45_CallInfoArgAccessFailsForNull 6 | { 7 | public interface IAmAnInterface 8 | { 9 | bool ThatHasAMethodWithArgs(string s, object o); 10 | } 11 | 12 | [Test] 13 | public void Should_be_able_to_find_a_null_arg_by_type() 14 | { 15 | string stringArgumentUsed = ""; 16 | 17 | var sub = Substitute.For(); 18 | sub.ThatHasAMethodWithArgs(null, null) 19 | .ReturnsForAnyArgs(x => { stringArgumentUsed = x.Arg(); return true; }); 20 | 21 | sub.ThatHasAMethodWithArgs(null, 42); 22 | 23 | Assert.That(stringArgumentUsed, Is.Null); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue47_RaisingEventsWithNullArg.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue47_RaisingEventsWithNullArg 6 | { 7 | public delegate void ProgressEventHandler(int progress, string message); 8 | public delegate void EventLikeHandler(object sender, EventArgs args); 9 | public interface IFoo 10 | { 11 | event ProgressEventHandler OnProgress; 12 | event EventLikeHandler OnEventishThing; 13 | } 14 | 15 | [Test] 16 | public void Pass_null_when_raising_delegate_event() 17 | { 18 | var sub = Substitute.For(); 19 | sub.OnProgress += Raise.Event(1, null); 20 | } 21 | 22 | [Test] 23 | public void Pass_null_when_raising_eventhandlerish_event() 24 | { 25 | var sub = Substitute.For(); 26 | sub.OnEventishThing += Raise.Event([null]); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue57_SettingVirtualPropertyInCtor.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue57_SettingVirtualPropertyInCtorCausesReturnsToUseAutoSub 6 | { 7 | public class MyEntity 8 | { 9 | public MyEntity() { Name = "Name1"; } 10 | public virtual Guid Id { get; set; } 11 | public virtual string Name { get; set; } 12 | } 13 | 14 | public interface IEntityRepository { MyEntity Get(Guid id); } 15 | 16 | [Test] 17 | public void TestGetFromRepository() 18 | { 19 | var repository = Substitute.For(); 20 | var fakeEntity = new MyEntity { Id = Guid.NewGuid() }; 21 | 22 | repository.Get(Arg.Any()).Returns(fakeEntity); 23 | 24 | var result = repository.Get(fakeEntity.Id); 25 | Assert.That(result, Is.SameAs(fakeEntity)); 26 | Assert.That(result.Id, Is.EqualTo(fakeEntity.Id)); 27 | } 28 | 29 | [Test] 30 | public void TestGetUsingAutoSubs() 31 | { 32 | var repository = Substitute.For(); 33 | var fakeEntity = repository.Get(Arg.Any()); 34 | fakeEntity.Id = Guid.NewGuid(); 35 | 36 | var result = repository.Get(fakeEntity.Id); 37 | 38 | Assert.That(result, Is.SameAs(fakeEntity)); 39 | Assert.That(result.Id, Is.EqualTo(fakeEntity.Id)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue61_ArgAnyStringRegression.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class ArgAnyStringRegression 7 | { 8 | public interface IFoo { string Bar(string a, double b); } 9 | 10 | [Test] 11 | public void Stub_any_string_and_call_with_null() 12 | { 13 | var foo = Substitute.For(); 14 | foo.Bar(Arg.Any(), Arg.Any()).ReturnsForAnyArgs("hello"); 15 | 16 | ClassicAssert.AreEqual("hello", foo.Bar(null, 0)); 17 | } 18 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue631_NamespaceDelegate.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Na { public delegate void DoNothing(); } 4 | namespace Ns { public delegate void DoNothing(); } 5 | namespace Nt { public delegate void DoNothing(); } 6 | namespace Nz { public delegate void DoNothing(); } 7 | 8 | namespace NSubstitute.Acceptance.Specs.FieldReports 9 | { 10 | public class Issue631_NamespaceDelegate 11 | { 12 | [Test] public void Na() { Substitute.For(); } 13 | [Test] public void Ns() { Substitute.For(); } 14 | [Test] public void Nt() { Substitute.For(); } 15 | [Test] public void Nz() { Substitute.For(); } 16 | } 17 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue83_MethodsWithGenericStructConstraint.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace NSubstitute.Acceptance.Specs.FieldReports; 4 | 5 | public class Issue83_MethodsWithGenericStructConstraint 6 | { 7 | public interface IService { T Get(T arg) where T : struct; } 8 | 9 | [Test] 10 | public void TestGenericCalls() 11 | { 12 | var id = Guid.NewGuid(); 13 | var service = Substitute.For(); 14 | service.Get(id); 15 | service.Received().Get(id); 16 | } 17 | } -------------------------------------------------------------------------------- /tests/NSubstitute.Acceptance.Specs/FieldReports/Issue_RaiseEventOnNonSubstitute.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute.Exceptions; 2 | using NUnit.Framework; 3 | 4 | namespace NSubstitute.Acceptance.Specs.FieldReports; 5 | 6 | public class Issue_RaiseEventOnNonSubstitute 7 | { 8 | public class Button 9 | { 10 | public virtual event EventHandler Clicked = (s, e) => { }; 11 | } 12 | 13 | public interface IController { void Load(); } 14 | 15 | [Test] 16 | public void RaiseEventOnNonSub() 17 | { 18 | var notASub = new Button(); 19 | notASub.Clicked += Raise.Event(); 20 | var sub = Substitute.For(); 21 | // Next call to a substitute will fail as it will attempt to raise an event 22 | Assert.Throws(() => 23 | sub.Load() 24 | ); 25 | } 26 | 27 | [Test] 28 | public void RaiseEventOnSub() 29 | { 30 | var clicked = false; 31 | var sub = Substitute.For