diff --git a/build/Common.tests.props b/build/Common.tests.props index 675f4e50..7ed10e63 100644 --- a/build/Common.tests.props +++ b/build/Common.tests.props @@ -25,7 +25,7 @@ [3.1.2] [6.7.0] [17.2.0] - [4.18.1] + [5.0.0] [2.4.3,3.0) [2.4.1,3.0) diff --git a/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj b/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj index 5368701e..69ee2770 100644 --- a/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj +++ b/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj @@ -23,7 +23,6 @@ - runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/OpenFeature.Tests/FeatureProviderTests.cs b/test/OpenFeature.Tests/FeatureProviderTests.cs index ba159a97..9a355bbb 100644 --- a/test/OpenFeature.Tests/FeatureProviderTests.cs +++ b/test/OpenFeature.Tests/FeatureProviderTests.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using AutoFixture; using FluentAssertions; -using Moq; +using NSubstitute; using OpenFeature.Constant; using OpenFeature.Model; using OpenFeature.Tests.Internal; @@ -79,67 +79,62 @@ public async Task Provider_Must_ErrorType() var defaultIntegerValue = fixture.Create(); var defaultDoubleValue = fixture.Create(); var defaultStructureValue = fixture.Create(); - var providerMock = new Mock(MockBehavior.Strict); + var providerMock = Substitute.For(); const string testMessage = "An error message"; - providerMock.Setup(x => x.ResolveBooleanValue(flagName, defaultBoolValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultBoolValue, ErrorType.General, + providerMock.ResolveBooleanValue(flagName, defaultBoolValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName, defaultBoolValue, ErrorType.General, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => x.ResolveIntegerValue(flagName, defaultIntegerValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.ParseError, + providerMock.ResolveIntegerValue(flagName, defaultIntegerValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.ParseError, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => x.ResolveDoubleValue(flagName, defaultDoubleValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.InvalidContext, + providerMock.ResolveDoubleValue(flagName, defaultDoubleValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.InvalidContext, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => x.ResolveStringValue(flagName, defaultStringValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultStringValue, ErrorType.TypeMismatch, + providerMock.ResolveStringValue(flagName, defaultStringValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName, defaultStringValue, ErrorType.TypeMismatch, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => - x.ResolveStructureValue(flagName, defaultStructureValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultStructureValue, ErrorType.FlagNotFound, + providerMock.ResolveStructureValue(flagName, defaultStructureValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName, defaultStructureValue, ErrorType.FlagNotFound, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => - x.ResolveStructureValue(flagName2, defaultStructureValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName2, defaultStructureValue, ErrorType.ProviderNotReady, + providerMock.ResolveStructureValue(flagName2, defaultStructureValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName2, defaultStructureValue, ErrorType.ProviderNotReady, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.Setup(x => x.ResolveBooleanValue(flagName2, defaultBoolValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName2, defaultBoolValue, ErrorType.TargetingKeyMissing, + providerMock.ResolveBooleanValue(flagName2, defaultBoolValue, Arg.Any()) + .Returns(new ResolutionDetails(flagName2, defaultBoolValue, ErrorType.TargetingKeyMissing, NoOpProvider.ReasonNoOp, NoOpProvider.Variant)); - - var provider = providerMock.Object; - - var boolRes = await provider.ResolveBooleanValue(flagName, defaultBoolValue); + var boolRes = await providerMock.ResolveBooleanValue(flagName, defaultBoolValue); boolRes.ErrorType.Should().Be(ErrorType.General); boolRes.ErrorMessage.Should().Be(testMessage); - var intRes = await provider.ResolveIntegerValue(flagName, defaultIntegerValue); + var intRes = await providerMock.ResolveIntegerValue(flagName, defaultIntegerValue); intRes.ErrorType.Should().Be(ErrorType.ParseError); intRes.ErrorMessage.Should().Be(testMessage); - var doubleRes = await provider.ResolveDoubleValue(flagName, defaultDoubleValue); + var doubleRes = await providerMock.ResolveDoubleValue(flagName, defaultDoubleValue); doubleRes.ErrorType.Should().Be(ErrorType.InvalidContext); doubleRes.ErrorMessage.Should().Be(testMessage); - var stringRes = await provider.ResolveStringValue(flagName, defaultStringValue); + var stringRes = await providerMock.ResolveStringValue(flagName, defaultStringValue); stringRes.ErrorType.Should().Be(ErrorType.TypeMismatch); stringRes.ErrorMessage.Should().Be(testMessage); - var structRes1 = await provider.ResolveStructureValue(flagName, defaultStructureValue); + var structRes1 = await providerMock.ResolveStructureValue(flagName, defaultStructureValue); structRes1.ErrorType.Should().Be(ErrorType.FlagNotFound); structRes1.ErrorMessage.Should().Be(testMessage); - var structRes2 = await provider.ResolveStructureValue(flagName2, defaultStructureValue); + var structRes2 = await providerMock.ResolveStructureValue(flagName2, defaultStructureValue); structRes2.ErrorType.Should().Be(ErrorType.ProviderNotReady); structRes2.ErrorMessage.Should().Be(testMessage); - var boolRes2 = await provider.ResolveBooleanValue(flagName2, defaultBoolValue); + var boolRes2 = await providerMock.ResolveBooleanValue(flagName2, defaultBoolValue); boolRes2.ErrorType.Should().Be(ErrorType.TargetingKeyMissing); boolRes2.ErrorMessage.Should().BeNull(); } diff --git a/test/OpenFeature.Tests/OpenFeature.Tests.csproj b/test/OpenFeature.Tests/OpenFeature.Tests.csproj index 7f630ef0..6c719638 100644 --- a/test/OpenFeature.Tests/OpenFeature.Tests.csproj +++ b/test/OpenFeature.Tests/OpenFeature.Tests.csproj @@ -18,7 +18,8 @@ - + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/OpenFeature.Tests/OpenFeatureClientTests.cs b/test/OpenFeature.Tests/OpenFeatureClientTests.cs index aec5a631..fef7403a 100644 --- a/test/OpenFeature.Tests/OpenFeatureClientTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureClientTests.cs @@ -5,7 +5,9 @@ using AutoFixture; using FluentAssertions; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Logging.Internal; +using NSubstitute; +using NSubstitute.ExceptionExtensions; using OpenFeature.Constant; using OpenFeature.Error; using OpenFeature.Model; @@ -22,9 +24,9 @@ public void OpenFeatureClient_Should_Allow_Hooks() { var fixture = new Fixture(); var clientName = fixture.Create(); - var hook1 = new Mock(MockBehavior.Strict).Object; - var hook2 = new Mock(MockBehavior.Strict).Object; - var hook3 = new Mock(MockBehavior.Strict).Object; + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); + var hook3 = Substitute.For(); var client = Api.Instance.GetClient(clientName); @@ -160,35 +162,28 @@ public async Task OpenFeatureClient_Should_Return_DefaultValue_When_Type_Mismatc var clientVersion = fixture.Create(); var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var mockedFeatureProvider = new Mock(MockBehavior.Strict); - var mockedLogger = new Mock>(MockBehavior.Default); + var mockedFeatureProvider = Substitute.For(); + var mockedLogger = Substitute.For>(); // This will fail to case a String to TestStructure - mockedFeatureProvider - .Setup(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny())) - .Throws(); - mockedFeatureProvider.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - mockedFeatureProvider.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + mockedFeatureProvider.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Throws(); + mockedFeatureProvider.GetMetadata().Returns(new Metadata(fixture.Create())); + mockedFeatureProvider.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(mockedFeatureProvider.Object); - var client = Api.Instance.GetClient(clientName, clientVersion, mockedLogger.Object); + Api.Instance.SetProvider(mockedFeatureProvider); + var client = Api.Instance.GetClient(clientName, clientVersion, mockedLogger); var evaluationDetails = await client.GetObjectDetails(flagName, defaultValue); evaluationDetails.ErrorType.Should().Be(ErrorType.TypeMismatch); - mockedFeatureProvider - .Verify(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny()), Times.Once); - - mockedLogger.Verify( - x => x.Log( - LogLevel.Error, - It.IsAny(), - It.Is((o, t) => string.Equals($"Error while evaluating flag {flagName}", o.ToString(), StringComparison.InvariantCultureIgnoreCase)), - It.IsAny(), - It.IsAny>()), - Times.Once); + _ = mockedFeatureProvider.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); + + mockedLogger.Received(1).Log( + LogLevel.Error, + Arg.Any(), + Arg.Is(t => string.Equals($"Error while evaluating flag {flagName}", t.ToString(), StringComparison.InvariantCultureIgnoreCase)), + Arg.Any(), + Arg.Any>()); } [Fact] @@ -200,21 +195,17 @@ public async Task Should_Resolve_BooleanValue() var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveBooleanValue(flagName, defaultValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultValue)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveBooleanValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); (await client.GetBooleanValue(flagName, defaultValue)).Should().Be(defaultValue); - featureProviderMock.Verify(x => x.ResolveBooleanValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveBooleanValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -226,21 +217,17 @@ public async Task Should_Resolve_StringValue() var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveStringValue(flagName, defaultValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultValue)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveStringValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); (await client.GetStringValue(flagName, defaultValue)).Should().Be(defaultValue); - featureProviderMock.Verify(x => x.ResolveStringValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveStringValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -252,21 +239,17 @@ public async Task Should_Resolve_IntegerValue() var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveIntegerValue(flagName, defaultValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultValue)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveIntegerValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); (await client.GetIntegerValue(flagName, defaultValue)).Should().Be(defaultValue); - featureProviderMock.Verify(x => x.ResolveIntegerValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveIntegerValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -278,21 +261,17 @@ public async Task Should_Resolve_DoubleValue() var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveDoubleValue(flagName, defaultValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultValue)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveDoubleValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); (await client.GetDoubleValue(flagName, defaultValue)).Should().Be(defaultValue); - featureProviderMock.Verify(x => x.ResolveDoubleValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveDoubleValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -304,21 +283,17 @@ public async Task Should_Resolve_StructureValue() var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny())) - .ReturnsAsync(new ResolutionDetails(flagName, defaultValue)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); (await client.GetObjectValue(flagName, defaultValue)).Should().Be(defaultValue); - featureProviderMock.Verify(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -331,24 +306,19 @@ public async Task When_Error_Is_Returned_From_Provider_Should_Return_Error() var defaultValue = fixture.Create(); const string testMessage = "Couldn't parse flag data."; - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny())) - .Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, - "ERROR", null, testMessage))); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); - - Api.Instance.SetProvider(featureProviderMock.Object); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, "ERROR", null, testMessage))); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); + + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); var response = await client.GetObjectDetails(flagName, defaultValue); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - featureProviderMock.Verify(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); } [Fact] @@ -361,23 +331,19 @@ public async Task When_Exception_Occurs_During_Evaluation_Should_Return_Error() var defaultValue = fixture.Create(); const string testMessage = "Couldn't parse flag data."; - var featureProviderMock = new Mock(MockBehavior.Strict); - featureProviderMock - .Setup(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny())) - .Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); - featureProviderMock.Setup(x => x.GetMetadata()) - .Returns(new Metadata(fixture.Create())); - featureProviderMock.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProviderMock = Substitute.For(); + featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); + featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); + featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - Api.Instance.SetProvider(featureProviderMock.Object); + Api.Instance.SetProvider(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); var response = await client.GetObjectDetails(flagName, defaultValue); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - featureProviderMock.Verify(x => x.ResolveStructureValue(flagName, defaultValue, It.IsAny()), Times.Once); + _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); } [Fact] diff --git a/test/OpenFeature.Tests/OpenFeatureHookTests.cs b/test/OpenFeature.Tests/OpenFeatureHookTests.cs index df127790..e81c5d3c 100644 --- a/test/OpenFeature.Tests/OpenFeatureHookTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureHookTests.cs @@ -6,6 +6,8 @@ using AutoFixture; using FluentAssertions; using Moq; +using NSubstitute; +using NSubstitute.ExceptionExtensions; using OpenFeature.Constant; using OpenFeature.Model; using OpenFeature.Tests.Internal; @@ -26,102 +28,63 @@ public async Task Hooks_Should_Be_Called_In_Order() var clientVersion = fixture.Create(); var flagName = fixture.Create(); var defaultValue = fixture.Create(); - var apiHook = new Mock(MockBehavior.Strict); - var clientHook = new Mock(MockBehavior.Strict); - var invocationHook = new Mock(MockBehavior.Strict); - var providerHook = new Mock(MockBehavior.Strict); - - var sequence = new MockSequence(); - - apiHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(), - It.IsAny>())) - .ReturnsAsync(EvaluationContext.Empty); - - clientHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(), - It.IsAny>())) - .ReturnsAsync(EvaluationContext.Empty); - - invocationHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(), - It.IsAny>())) - .ReturnsAsync(EvaluationContext.Empty); - - providerHook.InSequence(sequence).Setup(x => x.Before(It.IsAny>(), - It.IsAny>())) - .ReturnsAsync(EvaluationContext.Empty); - - providerHook.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - invocationHook.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - clientHook.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - apiHook.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - providerHook.InSequence(sequence).Setup(x => x.Finally(It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - invocationHook.InSequence(sequence).Setup(x => x.Finally(It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - clientHook.InSequence(sequence).Setup(x => x.Finally(It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); - - apiHook.InSequence(sequence).Setup(x => x.Finally(It.IsAny>(), - It.IsAny>())).Returns(Task.CompletedTask); + var apiHook = Substitute.For(); + var clientHook = Substitute.For(); + var invocationHook = Substitute.For(); + var providerHook = Substitute.For(); + + // Sequence + apiHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + clientHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + invocationHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + providerHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + providerHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + invocationHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + clientHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + apiHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + providerHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + invocationHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + clientHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + apiHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); var testProvider = new TestProvider(); - testProvider.AddHook(providerHook.Object); - Api.Instance.AddHooks(apiHook.Object); + testProvider.AddHook(providerHook); + Api.Instance.AddHooks(apiHook); Api.Instance.SetProvider(testProvider); var client = Api.Instance.GetClient(clientName, clientVersion); - client.AddHooks(clientHook.Object); + client.AddHooks(clientHook); await client.GetBooleanValue(flagName, defaultValue, EvaluationContext.Empty, - new FlagEvaluationOptions(invocationHook.Object, ImmutableDictionary.Empty)); - - apiHook.Verify(x => x.Before( - It.IsAny>(), It.IsAny>()), Times.Once); - - clientHook.Verify(x => x.Before( - It.IsAny>(), It.IsAny>()), Times.Once); - - invocationHook.Verify(x => x.Before( - It.IsAny>(), It.IsAny>()), Times.Once); - - providerHook.Verify(x => x.Before( - It.IsAny>(), It.IsAny>()), Times.Once); - - providerHook.Verify(x => x.After( - It.IsAny>(), It.IsAny>(), It.IsAny>()), Times.Once); - - invocationHook.Verify(x => x.After( - It.IsAny>(), It.IsAny>(), It.IsAny>()), Times.Once); - - clientHook.Verify(x => x.After( - It.IsAny>(), It.IsAny>(), It.IsAny>()), Times.Once); - - apiHook.Verify(x => x.After( - It.IsAny>(), It.IsAny>(), It.IsAny>()), Times.Once); - - providerHook.Verify(x => x.Finally( - It.IsAny>(), It.IsAny>()), Times.Once); - - invocationHook.Verify(x => x.Finally( - It.IsAny>(), It.IsAny>()), Times.Once); - - clientHook.Verify(x => x.Finally( - It.IsAny>(), It.IsAny>()), Times.Once); + new FlagEvaluationOptions(invocationHook, ImmutableDictionary.Empty)); - apiHook.Verify(x => x.Finally( - It.IsAny>(), It.IsAny>()), Times.Once); + Received.InOrder(() => + { + apiHook.Before(Arg.Any>(), Arg.Any>()); + clientHook.Before(Arg.Any>(), Arg.Any>()); + invocationHook.Before(Arg.Any>(), Arg.Any>()); + providerHook.Before(Arg.Any>(), Arg.Any>()); + providerHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + invocationHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + clientHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + apiHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + providerHook.Finally(Arg.Any>(), Arg.Any>()); + invocationHook.Finally(Arg.Any>(), Arg.Any>()); + clientHook.Finally(Arg.Any>(), Arg.Any>()); + apiHook.Finally(Arg.Any>(), Arg.Any>()); + }); + + _ = apiHook.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = apiHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + _ = apiHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); } [Fact] @@ -169,25 +132,21 @@ public void Hook_Context_Should_Have_Properties_And_Be_Immutable() public async Task Evaluation_Context_Must_Be_Mutable_Before_Hook() { var evaluationContext = new EvaluationContextBuilder().Set("test", "test").Build(); - var hook1 = new Mock(MockBehavior.Strict); - var hook2 = new Mock(MockBehavior.Strict); + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); var hookContext = new HookContext("test", false, FlagValueType.Boolean, new ClientMetadata("test", "1.0.0"), new Metadata(NoOpProvider.NoOpProviderName), evaluationContext); - hook1.Setup(x => x.Before(It.IsAny>(), It.IsAny>())) - .ReturnsAsync(evaluationContext); - - hook2.Setup(x => - x.Before(hookContext, It.IsAny>())) - .ReturnsAsync(evaluationContext); + hook1.Before(Arg.Any>(), Arg.Any>()).Returns(evaluationContext); + hook2.Before(hookContext, Arg.Any>()).Returns(evaluationContext); var client = Api.Instance.GetClient("test", "1.0.0"); await client.GetBooleanValue("test", false, EvaluationContext.Empty, - new FlagEvaluationOptions(ImmutableList.Create(hook1.Object, hook2.Object), ImmutableDictionary.Empty)); + new FlagEvaluationOptions(ImmutableList.Create(hook1, hook2), ImmutableDictionary.Empty)); - hook1.Verify(x => x.Before(It.IsAny>(), It.IsAny>()), Times.Once); - hook2.Verify(x => x.Before(It.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), It.IsAny>()), Times.Once); + _ = hook1.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = hook2.Received(1).Before(Arg.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), Arg.Any>()); } [Fact] @@ -229,29 +188,25 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order() .Set(propInvocationToOverwrite, true) .Build(); - var provider = new Mock(MockBehavior.Strict); + var provider = Substitute.For(); - provider.Setup(x => x.GetMetadata()) - .Returns(new Metadata(null)); + provider.GetMetadata().Returns(new Metadata(null)); - provider.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + provider.GetProviderHooks().Returns(ImmutableList.Empty); - provider.Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new ResolutionDetails("test", true)); + provider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", true)); - Api.Instance.SetProvider(provider.Object); + Api.Instance.SetProvider(provider); - var hook = new Mock(MockBehavior.Strict); - hook.Setup(x => x.Before(It.IsAny>(), It.IsAny>())) - .ReturnsAsync(hookContext); + var hook = Substitute.For(); + hook.Before(Arg.Any>(), Arg.Any>()).Returns(hookContext); var client = Api.Instance.GetClient("test", "1.0.0", null, clientContext); - await client.GetBooleanValue("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook.Object), ImmutableDictionary.Empty)); + await client.GetBooleanValue("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook), ImmutableDictionary.Empty)); // after proper merging, all properties should equal true - provider.Verify(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.Is(y => + _ = provider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Is(y => (y.GetValue(propGlobal).AsBoolean ?? false) && (y.GetValue(propClient).AsBoolean ?? false) && (y.GetValue(propGlobalToOverwrite).AsBoolean ?? false) @@ -259,7 +214,7 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order() && (y.GetValue(propClientToOverwrite).AsBoolean ?? false) && (y.GetValue(propHook).AsBoolean ?? false) && (y.GetValue(propInvocationToOverwrite).AsBoolean ?? false) - )), Times.Once); + )); } [Fact] @@ -301,60 +256,55 @@ public async Task Hook_Should_Return_No_Errors() [Specification("4.5.3", "The hook MUST NOT alter the `hook hints` structure.")] public async Task Hook_Should_Execute_In_Correct_Order() { - var featureProvider = new Mock(MockBehavior.Strict); - var hook = new Mock(MockBehavior.Strict); + var featureProvider = Substitute.For(); + var hook = Substitute.For(); - var sequence = new MockSequence(); + featureProvider.GetMetadata().Returns(new Metadata(null)); + featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); - featureProvider.Setup(x => x.GetMetadata()) - .Returns(new Metadata(null)); + // Sequence + hook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); + _ = hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = hook.Finally(Arg.Any>(), Arg.Any>()); - featureProvider.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); - - hook.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), It.IsAny>())) - .ReturnsAsync(EvaluationContext.Empty); - - featureProvider.InSequence(sequence) - .Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new ResolutionDetails("test", false)); - - hook.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), It.IsAny>())); - - hook.InSequence(sequence).Setup(x => - x.Finally(It.IsAny>(), It.IsAny>())); - - Api.Instance.SetProvider(featureProvider.Object); + Api.Instance.SetProvider(featureProvider); var client = Api.Instance.GetClient(); - client.AddHooks(hook.Object); + client.AddHooks(hook); await client.GetBooleanValue("test", false); - hook.Verify(x => x.Before(It.IsAny>(), It.IsAny>()), Times.Once); - hook.Verify(x => x.After(It.IsAny>(), It.IsAny>(), It.IsAny>()), Times.Once); - hook.Verify(x => x.Finally(It.IsAny>(), It.IsAny>()), Times.Once); - featureProvider.Verify(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + Received.InOrder(() => + { + hook.Before(Arg.Any>(), Arg.Any>()); + featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); + hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + hook.Finally(Arg.Any>(), Arg.Any>()); + }); + + _ = hook.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = hook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = hook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + _ = featureProvider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] [Specification("4.4.1", "The API, Client, Provider, and invocation MUST have a method for registering hooks.")] public async Task Register_Hooks_Should_Be_Available_At_All_Levels() { - var hook1 = new Mock(MockBehavior.Strict); - var hook2 = new Mock(MockBehavior.Strict); - var hook3 = new Mock(MockBehavior.Strict); - var hook4 = new Mock(MockBehavior.Strict); + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); + var hook3 = Substitute.For(); + var hook4 = Substitute.For(); var testProvider = new TestProvider(); - testProvider.AddHook(hook4.Object); - Api.Instance.AddHooks(hook1.Object); + testProvider.AddHook(hook4); + Api.Instance.AddHooks(hook1); Api.Instance.SetProvider(testProvider); var client = Api.Instance.GetClient(); - client.AddHooks(hook2.Object); + client.AddHooks(hook2); await client.GetBooleanValue("test", false, null, - new FlagEvaluationOptions(hook3.Object, ImmutableDictionary.Empty)); + new FlagEvaluationOptions(hook3, ImmutableDictionary.Empty)); Assert.Single(Api.Instance.GetHooks()); client.GetHooks().Count().Should().Be(1); @@ -365,144 +315,121 @@ await client.GetBooleanValue("test", false, null, [Specification("4.4.3", "If a `finally` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `finally` hooks.")] public async Task Finally_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() { - var featureProvider = new Mock(MockBehavior.Strict); - var hook1 = new Mock(MockBehavior.Strict); - var hook2 = new Mock(MockBehavior.Strict); - - var sequence = new MockSequence(); - - featureProvider.Setup(x => x.GetMetadata()) - .Returns(new Metadata(null)); - - featureProvider.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); - - hook1.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), null)) - .ReturnsAsync(EvaluationContext.Empty); - - hook2.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), null)) - .ReturnsAsync(EvaluationContext.Empty); - - featureProvider.InSequence(sequence) - .Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new ResolutionDetails("test", false)); - - hook2.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), null)) - .Returns(Task.CompletedTask); - - hook1.InSequence(sequence).Setup(x => x.After(It.IsAny>(), - It.IsAny>(), null)) - .Returns(Task.CompletedTask); - - hook2.InSequence(sequence).Setup(x => - x.Finally(It.IsAny>(), null)) - .Returns(Task.CompletedTask); - - hook1.InSequence(sequence).Setup(x => - x.Finally(It.IsAny>(), null)) - .Throws(new Exception()); - - Api.Instance.SetProvider(featureProvider.Object); + var featureProvider = Substitute.For(); + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); + + featureProvider.GetMetadata().Returns(new Metadata(null)); + featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); + + // Sequence + hook1.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); + hook2.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); + hook2.After(Arg.Any>(), Arg.Any>(), null).Returns(Task.CompletedTask); + hook1.After(Arg.Any>(), Arg.Any>(), null).Returns(Task.CompletedTask); + hook2.Finally(Arg.Any>(), null).Returns(Task.CompletedTask); + hook1.Finally(Arg.Any>(), null).Throws(new Exception()); + + Api.Instance.SetProvider(featureProvider); var client = Api.Instance.GetClient(); - client.AddHooks(new[] { hook1.Object, hook2.Object }); + client.AddHooks(new[] { hook1, hook2 }); client.GetHooks().Count().Should().Be(2); await client.GetBooleanValue("test", false); - hook1.Verify(x => x.Before(It.IsAny>(), null), Times.Once); - hook2.Verify(x => x.Before(It.IsAny>(), null), Times.Once); - featureProvider.Verify(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - hook2.Verify(x => x.After(It.IsAny>(), It.IsAny>(), null), Times.Once); - hook1.Verify(x => x.After(It.IsAny>(), It.IsAny>(), null), Times.Once); - hook2.Verify(x => x.Finally(It.IsAny>(), null), Times.Once); - hook1.Verify(x => x.Finally(It.IsAny>(), null), Times.Once); + Received.InOrder(() => + { + hook1.Before(Arg.Any>(), null); + hook2.Before(Arg.Any>(), null); + featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); + hook2.After(Arg.Any>(), Arg.Any>(), null); + hook1.After(Arg.Any>(), Arg.Any>(), null); + hook2.Finally(Arg.Any>(), null); + hook1.Finally(Arg.Any>(), null); + }); + + _ = hook1.Received(1).Before(Arg.Any>(), null); + _ = hook2.Received(1).Before(Arg.Any>(), null); + _ = featureProvider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); + _ = hook2.Received(1).After(Arg.Any>(), Arg.Any>(), null); + _ = hook1.Received(1).After(Arg.Any>(), Arg.Any>(), null); + _ = hook2.Received(1).Finally(Arg.Any>(), null); + _ = hook1.Received(1).Finally(Arg.Any>(), null); } [Fact] [Specification("4.4.4", "If an `error` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `error` hooks.")] public async Task Error_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() { - var featureProvider1 = new Mock(MockBehavior.Strict); - var hook1 = new Mock(MockBehavior.Strict); - var hook2 = new Mock(MockBehavior.Strict); - - var sequence = new MockSequence(); - - featureProvider1.Setup(x => x.GetMetadata()) - .Returns(new Metadata(null)); - featureProvider1.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProvider1 = Substitute.For(); + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); - hook1.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), null)) - .ReturnsAsync(EvaluationContext.Empty); + featureProvider1.GetMetadata().Returns(new Metadata(null)); + featureProvider1.GetProviderHooks().Returns(ImmutableList.Empty); - hook2.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), null)) - .ReturnsAsync(EvaluationContext.Empty); - - featureProvider1.InSequence(sequence) - .Setup(x => x.ResolveBooleanValue(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()); - - hook2.InSequence(sequence).Setup(x => - x.Error(It.IsAny>(), It.IsAny(), null)) - .Returns(Task.CompletedTask); - - hook1.InSequence(sequence).Setup(x => - x.Error(It.IsAny>(), It.IsAny(), null)) - .Returns(Task.CompletedTask); + // Sequence + hook1.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); + hook2.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); + featureProvider1.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new Exception()); + hook2.Error(Arg.Any>(), Arg.Any(), null).Returns(Task.CompletedTask); + hook1.Error(Arg.Any>(), Arg.Any(), null).Returns(Task.CompletedTask); - Api.Instance.SetProvider(featureProvider1.Object); + Api.Instance.SetProvider(featureProvider1); var client = Api.Instance.GetClient(); - client.AddHooks(new[] { hook1.Object, hook2.Object }); + client.AddHooks(new[] { hook1, hook2 }); await client.GetBooleanValue("test", false); - hook1.Verify(x => x.Before(It.IsAny>(), null), Times.Once); - hook2.Verify(x => x.Before(It.IsAny>(), null), Times.Once); - hook1.Verify(x => x.Error(It.IsAny>(), It.IsAny(), null), Times.Once); - hook2.Verify(x => x.Error(It.IsAny>(), It.IsAny(), null), Times.Once); + Received.InOrder(() => + { + hook1.Before(Arg.Any>(), null); + hook2.Before(Arg.Any>(), null); + featureProvider1.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); + hook2.Error(Arg.Any>(), Arg.Any(), null); + hook1.Error(Arg.Any>(), Arg.Any(), null); + }); + + _ = hook1.Received(1).Before(Arg.Any>(), null); + _ = hook2.Received(1).Before(Arg.Any>(), null); + _ = hook1.Received(1).Error(Arg.Any>(), Arg.Any(), null); + _ = hook2.Received(1).Error(Arg.Any>(), Arg.Any(), null); } [Fact] [Specification("4.4.6", "If an error occurs during the evaluation of `before` or `after` hooks, any remaining hooks in the `before` or `after` stages MUST NOT be invoked.")] public async Task Error_Occurs_During_Before_After_Evaluation_Should_Not_Invoke_Any_Remaining_Hooks() { - var featureProvider = new Mock(MockBehavior.Strict); - var hook1 = new Mock(MockBehavior.Strict); - var hook2 = new Mock(MockBehavior.Strict); - - var sequence = new MockSequence(); - - featureProvider.Setup(x => x.GetMetadata()) - .Returns(new Metadata(null)); - featureProvider.Setup(x => x.GetProviderHooks()) - .Returns(ImmutableList.Empty); + var featureProvider = Substitute.For(); + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); - hook1.InSequence(sequence).Setup(x => - x.Before(It.IsAny>(), It.IsAny>())) - .ThrowsAsync(new Exception()); + featureProvider.GetMetadata().Returns(new Metadata(null)); + featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); - hook1.InSequence(sequence).Setup(x => - x.Error(It.IsAny>(), It.IsAny(), null)); + // Sequence + hook1.Before(Arg.Any>(), Arg.Any>()).ThrowsAsync(new Exception()); + _ = hook1.Error(Arg.Any>(), Arg.Any(), null); + _ = hook2.Error(Arg.Any>(), Arg.Any(), null); - hook2.InSequence(sequence).Setup(x => - x.Error(It.IsAny>(), It.IsAny(), null)); - - Api.Instance.SetProvider(featureProvider.Object); + Api.Instance.SetProvider(featureProvider); var client = Api.Instance.GetClient(); - client.AddHooks(new[] { hook1.Object, hook2.Object }); + client.AddHooks(new[] { hook1, hook2 }); await client.GetBooleanValue("test", false); - hook1.Verify(x => x.Before(It.IsAny>(), It.IsAny>()), Times.Once); - hook2.Verify(x => x.Before(It.IsAny>(), It.IsAny>()), Times.Never); - hook1.Verify(x => x.Error(It.IsAny>(), It.IsAny(), null), Times.Once); - hook2.Verify(x => x.Error(It.IsAny>(), It.IsAny(), null), Times.Once); + Received.InOrder(() => + { + hook1.Before(Arg.Any>(), Arg.Any>()); + hook2.Error(Arg.Any>(), Arg.Any(), null); + hook1.Error(Arg.Any>(), Arg.Any(), null); + }); + + _ = hook1.Received(1).Before(Arg.Any>(), Arg.Any>()); + _ = hook2.DidNotReceive().Before(Arg.Any>(), Arg.Any>()); + _ = hook1.Received(1).Error(Arg.Any>(), Arg.Any(), null); + _ = hook2.Received(1).Error(Arg.Any>(), Arg.Any(), null); } [Fact] diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index f6ad03a0..9776d552 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -1,6 +1,6 @@ using System.Linq; using FluentAssertions; -using Moq; +using NSubstitute; using OpenFeature.Constant; using OpenFeature.Model; using OpenFeature.Tests.Internal; @@ -83,10 +83,10 @@ public void OpenFeature_Should_Allow_Multiple_Client_Names_Of_Same_Instance() public void OpenFeature_Should_Add_Hooks() { var openFeature = Api.Instance; - var hook1 = new Mock(MockBehavior.Strict).Object; - var hook2 = new Mock(MockBehavior.Strict).Object; - var hook3 = new Mock(MockBehavior.Strict).Object; - var hook4 = new Mock(MockBehavior.Strict).Object; + var hook1 = Substitute.For(); + var hook2 = Substitute.For(); + var hook3 = Substitute.For(); + var hook4 = Substitute.For(); openFeature.ClearHooks();