Skip to content

Commit

Permalink
test: Replace remaining Moq with NSubstitute (#143)
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Evenson <[email protected]>
  • Loading branch information
benjiro authored Aug 21, 2023
1 parent e9858d9 commit 598270d
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 55 deletions.
1 change: 0 additions & 1 deletion test/OpenFeature.Tests/OpenFeature.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
</PackageReference>
<PackageReference Include="FluentAssertions" Version="$(FluentAssertionsVer)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPkgVer)" />
<PackageReference Include="Moq" Version="[4.18.4]" />
<PackageReference Include="NSubstitute" Version="$(NSubstituteVer)" />
<PackageReference Include="xunit" Version="$(XUnitPkgVer)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioPkgVer)">
Expand Down
97 changes: 43 additions & 54 deletions test/OpenFeature.Tests/OpenFeatureHookTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading.Tasks;
using AutoFixture;
using FluentAssertions;
using Moq;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using OpenFeature.Constant;
Expand Down Expand Up @@ -436,45 +435,40 @@ public async Task Error_Occurs_During_Before_After_Evaluation_Should_Not_Invoke_
[Specification("4.5.1", "`Flag evaluation options` MAY contain `hook hints`, a map of data to be provided to hook invocations.")]
public async Task Hook_Hints_May_Be_Optional()
{
var featureProvider = new Mock<FeatureProvider>(MockBehavior.Strict);
var hook = new Mock<Hook>(MockBehavior.Strict);
var defaultEmptyHookHints = new Dictionary<string, object>();
var flagOptions = new FlagEvaluationOptions(hook.Object);
EvaluationContext evaluationContext = null;

var sequence = new MockSequence();
var featureProvider = Substitute.For<FeatureProvider>();
var hook = Substitute.For<Hook>();
var flagOptions = new FlagEvaluationOptions(hook);

featureProvider.Setup(x => x.GetMetadata())
featureProvider.GetMetadata()
.Returns(new Metadata(null));

featureProvider.Setup(x => x.GetProviderHooks())
featureProvider.GetProviderHooks()
.Returns(ImmutableList<Hook>.Empty);

hook.InSequence(sequence)
.Setup(x => x.Before(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints))
.ReturnsAsync(evaluationContext);
hook.Before(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(EvaluationContext.Empty);

featureProvider.InSequence(sequence)
.Setup(x => x.ResolveBooleanValue(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<EvaluationContext>()))
.ReturnsAsync(new ResolutionDetails<bool>("test", false));
featureProvider.ResolveBooleanValue("test", false, Arg.Any<EvaluationContext>())
.Returns(new ResolutionDetails<bool>("test", false));

hook.InSequence(sequence)
.Setup(x => x.After(It.IsAny<HookContext<It.IsAnyType>>(), It.IsAny<FlagEvaluationDetails<It.IsAnyType>>(), defaultEmptyHookHints))
.Returns(Task.CompletedTask);
hook.After(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(Task.FromResult(Task.CompletedTask));

hook.InSequence(sequence)
.Setup(x => x.Finally(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints))
hook.Finally(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(Task.CompletedTask);

Api.Instance.SetProvider(featureProvider.Object);
Api.Instance.SetProvider(featureProvider);
var client = Api.Instance.GetClient();

await client.GetBooleanValue("test", false, config: flagOptions);
await client.GetBooleanValue("test", false, EvaluationContext.Empty, flagOptions);

hook.Verify(x => x.Before(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
hook.Verify(x => x.After(It.IsAny<HookContext<It.IsAnyType>>(), It.IsAny<FlagEvaluationDetails<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
hook.Verify(x => x.Finally(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
featureProvider.Verify(x => x.ResolveBooleanValue(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<EvaluationContext>()), Times.Once);
Received.InOrder(() =>
{
hook.Received().Before(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
featureProvider.Received().ResolveBooleanValue("test", false, Arg.Any<EvaluationContext>());
hook.Received().After(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
hook.Received().Finally(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
});
}

[Fact]
Expand Down Expand Up @@ -515,52 +509,47 @@ public async Task When_Error_Occurs_In_Before_Hook_Should_Return_Default_Value()
[Specification("4.4.5", "If an error occurs in the `before` or `after` hooks, the `error` hooks MUST be invoked.")]
public async Task When_Error_Occurs_In_After_Hook_Should_Invoke_Error_Hook()
{
var featureProvider = new Mock<FeatureProvider>(MockBehavior.Strict);
var hook = new Mock<Hook>(MockBehavior.Strict);
var defaultEmptyHookHints = new Dictionary<string, object>();
var flagOptions = new FlagEvaluationOptions(hook.Object);
var featureProvider = Substitute.For<FeatureProvider>();
var hook = Substitute.For<Hook>();
var flagOptions = new FlagEvaluationOptions(hook);
var exceptionToThrow = new Exception("Fails during default");
EvaluationContext evaluationContext = null;

var sequence = new MockSequence();

featureProvider.Setup(x => x.GetMetadata())
featureProvider.GetMetadata()
.Returns(new Metadata(null));

featureProvider.Setup(x => x.GetProviderHooks())
featureProvider.GetProviderHooks()
.Returns(ImmutableList<Hook>.Empty);

hook.InSequence(sequence)
.Setup(x => x.Before(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints))
.ReturnsAsync(evaluationContext);
hook.Before(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(EvaluationContext.Empty);

featureProvider.InSequence(sequence)
.Setup(x => x.ResolveBooleanValue(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<EvaluationContext>()))
.ReturnsAsync(new ResolutionDetails<bool>("test", false));
featureProvider.ResolveBooleanValue(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<EvaluationContext>())
.Returns(new ResolutionDetails<bool>("test", false));

hook.InSequence(sequence)
.Setup(x => x.After(It.IsAny<HookContext<It.IsAnyType>>(), It.IsAny<FlagEvaluationDetails<It.IsAnyType>>(), defaultEmptyHookHints))
hook.After(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.ThrowsAsync(exceptionToThrow);

hook.InSequence(sequence)
.Setup(x => x.Error(It.IsAny<HookContext<It.IsAnyType>>(), It.IsAny<Exception>(), defaultEmptyHookHints))
hook.Error(Arg.Any<HookContext<bool>>(), Arg.Any<Exception>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(Task.CompletedTask);

hook.InSequence(sequence)
.Setup(x => x.Finally(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints))
hook.Finally(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
.Returns(Task.CompletedTask);

Api.Instance.SetProvider(featureProvider.Object);
Api.Instance.SetProvider(featureProvider);
var client = Api.Instance.GetClient();

var resolvedFlag = await client.GetBooleanValue("test", true, config: flagOptions);

resolvedFlag.Should().BeTrue();
hook.Verify(x => x.Before(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
hook.Verify(x => x.After(It.IsAny<HookContext<It.IsAnyType>>(), It.IsAny<FlagEvaluationDetails<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
hook.Verify(x => x.Error(It.IsAny<HookContext<It.IsAnyType>>(), exceptionToThrow, defaultEmptyHookHints), Times.Once);
hook.Verify(x => x.Finally(It.IsAny<HookContext<It.IsAnyType>>(), defaultEmptyHookHints), Times.Once);
featureProvider.Verify(x => x.ResolveBooleanValue(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<EvaluationContext>()), Times.Once);

Received.InOrder(() =>
{
hook.Received(1).Before(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
hook.Received(1).After(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
hook.Received(1).Finally(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>());
});

await featureProvider.DidNotReceive().ResolveBooleanValue("test", false, Arg.Any<EvaluationContext>());
}
}
}

0 comments on commit 598270d

Please sign in to comment.