Skip to content

Commit ee882c6

Browse files
authored
refactor: add callback benchmarks (#600)
Adds a new BenchmarkDotNet benchmark suite to measure and compare “callback on void method” behavior across multiple .NET mocking libraries. **Changes:** - Introduces `CallbackBenchmarks` to benchmark callback setup + invocation for Mockolate, Moq, NSubstitute, FakeItEasy, Imposter, and TUnit.Mocks.
1 parent e4b54a6 commit ee882c6

6 files changed

Lines changed: 134 additions & 13 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using BenchmarkDotNet.Attributes;
2+
using FakeItEasy;
3+
using Imposter.Abstractions;
4+
using Mockolate.Benchmarks;
5+
using NSubstitute;
6+
using Arg = NSubstitute.Arg;
7+
8+
[assembly: GenerateImposter(typeof(CallbackBenchmarks.IMyCallbackInterface))]
9+
10+
namespace Mockolate.Benchmarks;
11+
#pragma warning disable CA1822 // Mark members as static
12+
/// <summary>
13+
/// In this benchmark we measure the case of an interface mock with a method callback: configure a callback on a
14+
/// method invocation and invoke it twice.
15+
/// </summary>
16+
public class CallbackBenchmarks : BenchmarksBase
17+
{
18+
/// <summary>
19+
/// <see href="https://awexpect.com/Mockolate" />
20+
/// </summary>
21+
[Benchmark(Baseline = true)]
22+
public int Callback_Mockolate()
23+
{
24+
int count = 0;
25+
IMyCallbackInterface sut = IMyCallbackInterface.CreateMock();
26+
sut.Mock.Setup.MyFunc(It.IsAny<int>()).Do(() => count++);
27+
28+
sut.MyFunc(1);
29+
sut.MyFunc(2);
30+
return count;
31+
}
32+
33+
/// <summary>
34+
/// <see href="https://github.com/devlooped/moq" />
35+
/// </summary>
36+
[Benchmark]
37+
public int Callback_Moq()
38+
{
39+
int count = 0;
40+
Moq.Mock<IMyCallbackInterface> mock = new();
41+
mock.Setup(x => x.MyFunc(Moq.It.IsAny<int>())).Callback(() => count++);
42+
43+
IMyCallbackInterface instance = mock.Object;
44+
instance.MyFunc(1);
45+
instance.MyFunc(2);
46+
return count;
47+
}
48+
49+
/// <summary>
50+
/// <see href="https://nsubstitute.github.io/" />
51+
/// </summary>
52+
[Benchmark]
53+
public int Callback_NSubstitute()
54+
{
55+
int count = 0;
56+
IMyCallbackInterface mock = Substitute.For<IMyCallbackInterface>();
57+
mock.When(x => x.MyFunc(Arg.Any<int>())).Do(_ => count++);
58+
59+
mock.MyFunc(1);
60+
mock.MyFunc(2);
61+
return count;
62+
}
63+
64+
/// <summary>
65+
/// <see href="https://fakeiteasy.github.io/" />
66+
/// </summary>
67+
[Benchmark]
68+
public int Callback_FakeItEasy()
69+
{
70+
int count = 0;
71+
IMyCallbackInterface mock = A.Fake<IMyCallbackInterface>();
72+
A.CallTo(() => mock.MyFunc(A<int>.Ignored)).Invokes(() => count++);
73+
74+
mock.MyFunc(1);
75+
mock.MyFunc(2);
76+
return count;
77+
}
78+
79+
/// <summary>
80+
/// <see href="https://github.com/themidnightgospel/Imposter" />
81+
/// </summary>
82+
[Benchmark]
83+
public int Callback_Imposter()
84+
{
85+
int count = 0;
86+
IMyCallbackInterfaceImposter imposter = IMyCallbackInterface.Imposter();
87+
imposter.MyFunc(Imposter.Abstractions.Arg<int>.Any()).Callback(_ => count++);
88+
89+
IMyCallbackInterface instance = imposter.Instance();
90+
instance.MyFunc(1);
91+
instance.MyFunc(2);
92+
return count;
93+
}
94+
95+
/// <summary>
96+
/// <see href="https://github.com/thomhurst/TUnit/" />
97+
/// </summary>
98+
[Benchmark]
99+
public int Callback_TUnitMocks()
100+
{
101+
int count = 0;
102+
Mock<IMyCallbackInterface> mock = TUnit.Mocks.Mock.Of<IMyCallbackInterface>();
103+
mock.MyFunc(Any<int>())
104+
.Callback(() => count++);
105+
106+
IMyCallbackInterface svc = mock.Object;
107+
svc.MyFunc(1);
108+
svc.MyFunc(2);
109+
return count;
110+
}
111+
112+
public interface IMyCallbackInterface
113+
{
114+
void MyFunc(int value);
115+
}
116+
}
117+
#pragma warning restore CA1822 // Mark members as static

Benchmarks/Mockolate.Benchmarks/CompleteEventBenchmarks.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
using BenchmarkDotNet.Attributes;
22
using FakeItEasy;
33
using Imposter.Abstractions;
4+
using Mockolate.Benchmarks;
45
using Mockolate.Verify;
56
using NSubstitute;
67
using Arg = NSubstitute.Arg;
78
using Times = Moq.Times;
89

9-
[assembly: GenerateImposter(typeof(Mockolate.Benchmarks.CompleteEventBenchmarks.IMyEventInterface))]
10+
[assembly: GenerateImposter(typeof(CompleteEventBenchmarks.IMyEventInterface))]
1011

1112
namespace Mockolate.Benchmarks;
1213
#pragma warning disable CA1822 // Mark members as static
1314
/// <summary>
1415
/// In this benchmark we check the case of an interface mock with an event, subscribe to the event and verify
15-
/// the subscription was recorded once.<br />
16+
/// the subscription was recorded once.
1617
/// </summary>
1718
public class CompleteEventBenchmarks : BenchmarksBase
1819
{

Benchmarks/Mockolate.Benchmarks/CompleteIndexerBenchmarks.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
using BenchmarkDotNet.Attributes;
22
using FakeItEasy;
33
using Imposter.Abstractions;
4+
using Mockolate.Benchmarks;
45
using Mockolate.Verify;
56
using NSubstitute;
67
using Arg = NSubstitute.Arg;
78
using Times = Moq.Times;
89

9-
[assembly: GenerateImposter(typeof(Mockolate.Benchmarks.CompleteIndexerBenchmarks.IMyIndexerInterface))]
10+
[assembly: GenerateImposter(typeof(CompleteIndexerBenchmarks.IMyIndexerInterface))]
1011

1112
namespace Mockolate.Benchmarks;
1213
#pragma warning disable CA1822 // Mark members as static
1314
/// <summary>
1415
/// In this benchmark we check the case of an interface mock with an indexer, setup the indexer and verify
15-
/// the getter was called once.<br />
16+
/// the getter was called once.
1617
/// </summary>
1718
public class CompleteIndexerBenchmarks : BenchmarksBase
1819
{

Benchmarks/Mockolate.Benchmarks/CompleteMethodBenchmarks.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
using BenchmarkDotNet.Attributes;
22
using FakeItEasy;
33
using Imposter.Abstractions;
4+
using Mockolate.Benchmarks;
45
using Mockolate.Verify;
56
using NSubstitute;
67
using Arg = NSubstitute.Arg;
78
using Times = Moq.Times;
89

9-
[assembly: GenerateImposter(typeof(Mockolate.Benchmarks.CompleteMethodBenchmarks.IMyMethodInterface))]
10+
[assembly: GenerateImposter(typeof(CompleteMethodBenchmarks.IMyMethodInterface))]
1011

1112
namespace Mockolate.Benchmarks;
1213
#pragma warning disable CA1822 // Mark members as static
1314
/// <summary>
1415
/// In this benchmark we check the simple case of an interface mock, setup a single method that gets called and
15-
/// verified to be called once.<br />
16+
/// verified to be called once.
1617
/// </summary>
1718
public class CompleteMethodBenchmarks : BenchmarksBase
1819
{
1920
/// <summary>
2021
/// <see href="https://awexpect.com/Mockolate" />
2122
/// </summary>
22-
[Benchmark]
23+
[Benchmark(Baseline = true)]
2324
public void Method_Mockolate()
2425
{
2526
IMyMethodInterface sut = IMyMethodInterface.CreateMock();

Benchmarks/Mockolate.Benchmarks/CompletePropertyBenchmarks.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
using BenchmarkDotNet.Attributes;
22
using FakeItEasy;
33
using Imposter.Abstractions;
4+
using Mockolate.Benchmarks;
45
using Mockolate.Verify;
56
using NSubstitute;
67
using Times = Moq.Times;
78

8-
[assembly: GenerateImposter(typeof(Mockolate.Benchmarks.CompletePropertyBenchmarks.IMyPropertyInterface))]
9+
[assembly: GenerateImposter(typeof(CompletePropertyBenchmarks.IMyPropertyInterface))]
910

1011
namespace Mockolate.Benchmarks;
1112
#pragma warning disable CA1822 // Mark members as static
1213
/// <summary>
1314
/// In this benchmark we check the case of an interface mock with a property, setup the property and verify
14-
/// the getter was called once.<br />
15+
/// the getter was called once.
1516
/// </summary>
16-
public class CompletePropertyBenchmarks
17+
public class CompletePropertyBenchmarks : BenchmarksBase
1718
{
1819
/// <summary>
1920
/// <see href="https://awexpect.com/Mockolate" />
2021
/// </summary>
21-
[Benchmark]
22+
[Benchmark(Baseline = true)]
2223
public void Property_Mockolate()
2324
{
2425
IMyPropertyInterface sut = IMyPropertyInterface.CreateMock();

Benchmarks/Mockolate.Benchmarks/Mockolate.Benchmarks.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
</ItemGroup>
1919

2020
<ItemGroup>
21-
<PackageReference Include="Microsoft.CodeAnalysis.Common" />
22-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
21+
<PackageReference Include="Microsoft.CodeAnalysis.Common"/>
22+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
2323
<PackageReference Include="Moq"/>
2424
<PackageReference Include="NSubstitute"/>
2525
<PackageReference Include="FakeItEasy"/>

0 commit comments

Comments
 (0)