Skip to content

Commit 0a203d0

Browse files
committed
Replace Moq with NSubstitute - Idempotency
1 parent 697b9f0 commit 0a203d0

File tree

2 files changed

+83
-73
lines changed

2 files changed

+83
-73
lines changed

libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/AWS.Lambda.Powertools.Idempotency.Tests.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99

1010
<ItemGroup>
1111
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.6.0" />
12-
<PackageReference Include="FluentAssertions" Version="6.7.0" />
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
12+
<PackageReference Include="FluentAssertions" Version="6.11.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
1414
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
1515
<PackageReference Include="Amazon.Lambda.TestUtilities" Version="2.0.0" />
16-
<PackageReference Include="Moq" Version="4.18.4" />
16+
<PackageReference Include="NSubstitute" Version="5.0.0" />
1717
<PackageReference Include="Testcontainers" Version="3.3.0" />
18-
<PackageReference Include="xunit" Version="2.4.2" />
19-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
18+
<PackageReference Include="xunit" Version="2.5.0" />
19+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
2020
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2121
<PrivateAssets>all</PrivateAssets>
2222
</PackageReference>
23-
<PackageReference Include="coverlet.collector" Version="3.2.0">
23+
<PackageReference Include="coverlet.collector" Version="6.0.0">
2424
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2525
<PrivateAssets>all</PrivateAssets>
2626
</PackageReference>

libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs

Lines changed: 77 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System;
17+
using System.Linq;
1718
using System.Text.Json;
1819
using System.Threading.Tasks;
1920
using Amazon.Lambda.TestUtilities;
@@ -23,7 +24,8 @@
2324
using AWS.Lambda.Powertools.Idempotency.Tests.Handlers;
2425
using AWS.Lambda.Powertools.Idempotency.Tests.Model;
2526
using FluentAssertions;
26-
using Moq;
27+
using NSubstitute;
28+
using NSubstitute.ExceptionExtensions;
2729
using Xunit;
2830

2931
[assembly: CollectionBehavior(DisableTestParallelization = true)]
@@ -39,10 +41,10 @@ public class IdempotentAspectTests : IDisposable
3941
public async Task Handle_WhenFirstCall_ShouldPutInStore(Type type)
4042
{
4143
//Arrange
42-
var store = new Mock<BasePersistenceStore>();
44+
var store = Substitute.For<BasePersistenceStore>();
4345
Idempotency.Configure(builder =>
4446
builder
45-
.WithPersistenceStore(store.Object)
47+
.WithPersistenceStore(store)
4648
.WithOptions(optionsBuilder => optionsBuilder.WithEventKeyJmesPath("Id"))
4749
);
4850

@@ -55,12 +57,18 @@ public async Task Handle_WhenFirstCall_ShouldPutInStore(Type type)
5557
//Assert
5658
basket.Products.Count.Should().Be(1);
5759
function.HandlerExecuted.Should().BeTrue();
60+
61+
await store.Received().SaveInProgress(
62+
Arg.Is<JsonDocument>(t =>
63+
t.ToString() == JsonSerializer.SerializeToDocument(product, new JsonSerializerOptions()).ToString()),
64+
Arg.Any<DateTimeOffset>()
65+
);
5866

59-
store
60-
.Verify(x=>x.SaveInProgress(It.Is<JsonDocument>(t=> t.ToString() == JsonSerializer.SerializeToDocument(product, It.IsAny<JsonSerializerOptions>()).ToString()), It.IsAny<DateTimeOffset>()));
61-
62-
store
63-
.Verify(x=>x.SaveSuccess(It.IsAny<JsonDocument>(), It.Is<Basket>(y => y.Equals(basket)), It.IsAny<DateTimeOffset>()));
67+
await store.Received().SaveSuccess(
68+
Arg.Any<JsonDocument>(),
69+
Arg.Is<Basket>(y => y.Equals(basket)),
70+
Arg.Any<DateTimeOffset>()
71+
);
6472
}
6573

6674
[Theory]
@@ -69,14 +77,14 @@ public async Task Handle_WhenFirstCall_ShouldPutInStore(Type type)
6977
public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore(Type type)
7078
{
7179
//Arrange
72-
var store = new Mock<BasePersistenceStore>();
73-
store.Setup(x=>x.SaveInProgress(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
74-
.Throws<IdempotencyItemAlreadyExistsException>();
80+
var store = Substitute.For<BasePersistenceStore>();
81+
store.SaveInProgress(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>())
82+
.Returns(_ => throw new IdempotencyItemAlreadyExistsException());
7583

7684
// GIVEN
7785
Idempotency.Configure(builder =>
7886
builder
79-
.WithPersistenceStore(store.Object)
87+
.WithPersistenceStore(store)
8088
.WithOptions(optionsBuilder => optionsBuilder.WithEventKeyJmesPath("Id"))
8189
);
8290

@@ -88,9 +96,8 @@ public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore(Type ty
8896
DateTimeOffset.UtcNow.AddSeconds(356).ToUnixTimeSeconds(),
8997
JsonSerializer.SerializeToNode(basket)!.ToString(),
9098
null);
91-
store.Setup(x=>x.GetRecord(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
92-
.ReturnsAsync(record);
93-
99+
store.GetRecord(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>()).Returns(record);
100+
94101
var function = Activator.CreateInstance(type) as IIdempotencyEnabledFunction;
95102

96103
// Act
@@ -107,15 +114,16 @@ public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore(Type ty
107114
public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempotencyAlreadyInProgressException(Type type)
108115
{
109116
// Arrange
110-
var store = new Mock<BasePersistenceStore>();
111-
117+
var store = Substitute.For<BasePersistenceStore>();
118+
112119
Idempotency.Configure(builder =>
113120
builder
114-
.WithPersistenceStore(store.Object)
121+
.WithPersistenceStore(store)
115122
.WithOptions(optionsBuilder => optionsBuilder.WithEventKeyJmesPath("Id"))
116123
);
117-
store.Setup(x=>x.SaveInProgress(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
118-
.Throws<IdempotencyItemAlreadyExistsException>();
124+
125+
store.SaveInProgress(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>())
126+
.Returns(_ => throw new IdempotencyItemAlreadyExistsException());
119127

120128
var product = new Product(42, "fake product", 12);
121129
var basket = new Basket(product);
@@ -125,9 +133,9 @@ public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempoten
125133
DateTimeOffset.UtcNow.AddSeconds(356).ToUnixTimeSeconds(),
126134
JsonSerializer.SerializeToNode(basket)!.ToString(),
127135
null);
128-
store.Setup(x=>x.GetRecord(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
129-
.ReturnsAsync(record);
130-
136+
store.GetRecord(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>())
137+
.Returns(record);
138+
131139
// Act
132140
var function = Activator.CreateInstance(type) as IIdempotencyEnabledFunction;
133141
Func<Task> act = async () => await function!.HandleTest(product, new TestLambdaContext());
@@ -142,11 +150,11 @@ public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempoten
142150
public async Task Handle_WhenThrowException_ShouldDeleteRecord_AndThrowFunctionException(Type type)
143151
{
144152
// Arrange
145-
var store = new Mock<BasePersistenceStore>();
153+
var store = Substitute.For<BasePersistenceStore>();
146154

147155
Idempotency.Configure(builder =>
148156
builder
149-
.WithPersistenceStore(store.Object)
157+
.WithPersistenceStore(store)
150158
.WithOptions(optionsBuilder => optionsBuilder.WithEventKeyJmesPath("Id"))
151159
);
152160

@@ -158,24 +166,22 @@ public async Task Handle_WhenThrowException_ShouldDeleteRecord_AndThrowFunctionE
158166

159167
// Assert
160168
await act.Should().ThrowAsync<IndexOutOfRangeException>();
161-
store.Verify(
162-
x => x.DeleteRecord(It.IsAny<JsonDocument>(), It.IsAny<IndexOutOfRangeException>()));
169+
await store.Received().DeleteRecord(Arg.Any<JsonDocument>(), Arg.Any<IndexOutOfRangeException>());
163170
}
164171

165172
[Theory]
166173
[InlineData(typeof(IdempotencyEnabledFunction))]
167174
[InlineData(typeof(IdempotencyEnabledSyncFunction))]
168175
public async Task Handle_WhenIdempotencyDisabled_ShouldJustRunTheFunction(Type type)
169176
{
170-
171177
// Arrange
172-
var store = new Mock<BasePersistenceStore>();
178+
var store = Substitute.For<BasePersistenceStore>();
173179

174180
Environment.SetEnvironmentVariable(Constants.IdempotencyDisabledEnv, "true");
175181

176182
Idempotency.Configure(builder =>
177183
builder
178-
.WithPersistenceStore(store.Object)
184+
.WithPersistenceStore(store)
179185
.WithOptions(optionsBuilder => optionsBuilder.WithEventKeyJmesPath("Id"))
180186
);
181187

@@ -186,7 +192,7 @@ public async Task Handle_WhenIdempotencyDisabled_ShouldJustRunTheFunction(Type t
186192
var basket = await function!.HandleTest(product, new TestLambdaContext());
187193

188194
// Assert
189-
store.Invocations.Count.Should().Be(0);
195+
store.ReceivedCalls().Count().Should().Be(0);
190196
basket.Products.Count.Should().Be(1);
191197
function.HandlerExecuted.Should().BeTrue();
192198
}
@@ -198,72 +204,76 @@ public void Idempotency_Set_Execution_Environment_Context()
198204
var assemblyName = "AWS.Lambda.Powertools.Idempotency";
199205
var assemblyVersion = "1.0.0";
200206

201-
var env = new Mock<IPowertoolsEnvironment>();
202-
env.Setup(x => x.GetAssemblyName(It.IsAny<Idempotency>())).Returns(assemblyName);
203-
env.Setup(x => x.GetAssemblyVersion(It.IsAny<Idempotency>())).Returns(assemblyVersion);
207+
var env = Substitute.For<IPowertoolsEnvironment>();
208+
env.GetAssemblyName(Arg.Any<Idempotency>()).Returns(assemblyName);
209+
env.GetAssemblyVersion(Arg.Any<Idempotency>()).Returns(assemblyVersion);
204210

205-
var conf = new PowertoolsConfigurations(new SystemWrapper(env.Object));
211+
var conf = new PowertoolsConfigurations(new SystemWrapper(env));
206212

207213
// Act
208214
var xRayRecorder = new Idempotency(conf);
209215

210216
// Assert
211-
env.Verify(v =>
212-
v.SetEnvironmentVariable(
213-
"AWS_EXECUTION_ENV", $"{Constants.FeatureContextIdentifier}/Idempotency/{assemblyVersion}"
214-
), Times.Once);
215-
216-
env.Verify(v =>
217-
v.GetEnvironmentVariable(
218-
"AWS_EXECUTION_ENV"
219-
), Times.Once);
217+
env.Received(1).SetEnvironmentVariable(
218+
"AWS_EXECUTION_ENV",
219+
$"{Constants.FeatureContextIdentifier}/Idempotency/{assemblyVersion}"
220+
);
221+
222+
env.Received(1).GetEnvironmentVariable("AWS_EXECUTION_ENV");
220223

221224
Assert.NotNull(xRayRecorder);
222225
}
223-
226+
224227
[Fact]
225-
public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndFirstCall_ShouldPutInStore()
228+
public async Task Handle_WhenIdempotencyOnSubMethodAnnotated_AndFirstCall_ShouldPutInStore()
226229
{
227230
// Arrange
228-
var store = new Mock<BasePersistenceStore>();
229-
Idempotency.Configure(builder => builder.WithPersistenceStore(store.Object));
231+
var store = Substitute.For<BasePersistenceStore>();
232+
Idempotency.Configure(builder => builder.WithPersistenceStore(store));
230233

231234
// Act
232235
IdempotencyInternalFunction function = new IdempotencyInternalFunction();
233236
Product product = new Product(42, "fake product", 12);
234237
Basket resultBasket = function.HandleRequest(product, new TestLambdaContext());
235-
238+
236239
// Assert
237240
resultBasket.Products.Count.Should().Be(2);
238241
function.IsSubMethodCalled.Should().BeTrue();
239-
240-
store
241-
.Verify(x=>x.SaveInProgress(It.Is<JsonDocument>(t=> t.ToString() == JsonSerializer.SerializeToDocument("fake", It.IsAny<JsonSerializerOptions>()).ToString()), It.IsAny<DateTimeOffset>()));
242242

243-
store
244-
.Verify(x=>x.SaveSuccess(It.IsAny<JsonDocument>(), It.Is<Basket>(y => y.Equals(resultBasket)), It.IsAny<DateTimeOffset>()));
243+
await store
244+
.Received(1)
245+
.SaveInProgress(
246+
Arg.Is<JsonDocument>(t =>
247+
t.ToString() == JsonSerializer.SerializeToDocument("fake", new JsonSerializerOptions())
248+
.ToString()),
249+
Arg.Any<DateTimeOffset>());
250+
251+
await store
252+
.Received(1)
253+
.SaveSuccess(Arg.Any<JsonDocument>(), Arg.Is<Basket>(y => y.Equals(resultBasket)),
254+
Arg.Any<DateTimeOffset>());
245255
}
246256

247257
[Fact]
248258
public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndSecondCall_AndNotExpired_ShouldGetFromStore()
249259
{
250260
// Arrange
251-
var store = new Mock<BasePersistenceStore>();
252-
store.Setup(x => x.SaveInProgress(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
253-
.Throws<IdempotencyItemAlreadyExistsException>();
261+
var store = Substitute.For<BasePersistenceStore>();
262+
store.SaveInProgress(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>())
263+
.Throws(new IdempotencyItemAlreadyExistsException());
254264

255-
Idempotency.Configure(builder => builder.WithPersistenceStore(store.Object));
265+
Idempotency.Configure(builder => builder.WithPersistenceStore(store));
256266

257-
Product product = new Product(42, "fake product", 12);
258-
Basket basket = new Basket(product);
259-
DataRecord record = new DataRecord(
267+
var product = new Product(42, "fake product", 12);
268+
var basket = new Basket(product);
269+
var record = new DataRecord(
260270
"fake",
261271
DataRecord.DataRecordStatus.COMPLETED,
262272
DateTimeOffset.UtcNow.AddSeconds(356).ToUnixTimeSeconds(),
263273
JsonSerializer.SerializeToNode(basket)!.ToString(),
264274
null);
265-
store.Setup(x => x.GetRecord(It.IsAny<JsonDocument>(), It.IsAny<DateTimeOffset>()))
266-
.ReturnsAsync(record);
275+
store.GetRecord(Arg.Any<JsonDocument>(), Arg.Any<DateTimeOffset>())
276+
.Returns(record);
267277

268278
// Act
269279
var function = new IdempotencyInternalFunction();
@@ -298,10 +308,10 @@ public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndKeyJMESPath_ShouldPutI
298308
public void Handle_WhenIdempotencyOnSubMethodNotAnnotated_ShouldThrowException()
299309
{
300310
// Arrange
301-
var store = new Mock<BasePersistenceStore>();
311+
var store = Substitute.For<BasePersistenceStore>();
302312
Idempotency.Configure(builder =>
303313
builder
304-
.WithPersistenceStore(store.Object)
314+
.WithPersistenceStore(store)
305315
);
306316

307317
// Act
@@ -317,10 +327,10 @@ public void Handle_WhenIdempotencyOnSubMethodNotAnnotated_ShouldThrowException()
317327
public void Handle_WhenIdempotencyOnSubMethodVoid_ShouldThrowException()
318328
{
319329
// Arrange
320-
var store = new Mock<BasePersistenceStore>();
330+
var store = Substitute.For<BasePersistenceStore>();
321331
Idempotency.Configure(builder =>
322332
builder
323-
.WithPersistenceStore(store.Object)
333+
.WithPersistenceStore(store)
324334
);
325335

326336
// Act

0 commit comments

Comments
 (0)