diff --git a/Directory.Packages.props b/Directory.Packages.props index 3f1150b0fe..d49487b10b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -55,6 +55,8 @@ + + diff --git a/TUnit.Mocks.Benchmarks/CallbackBenchmarks.cs b/TUnit.Mocks.Benchmarks/CallbackBenchmarks.cs index df6822bc95..55b460abb4 100644 --- a/TUnit.Mocks.Benchmarks/CallbackBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/CallbackBenchmarks.cs @@ -1,5 +1,7 @@ using BenchmarkDotNet.Attributes; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; using Moq; using NSubstitute; @@ -25,12 +27,39 @@ public int TUnitMocks_Callback() return count; } + [Benchmark(Description = "Imposter")] + public int Imposter_Callback() + { + var count = 0; + var imposter = INotificationService.Imposter(); + imposter.Send(Arg.Any(), Arg.Any()) + .Callback((_, _) => count++); + + var instance = imposter.Instance(); + instance.Send("user@test.com", "Hello"); + instance.Send("user@test.com", "World"); + return count; + } + + [Benchmark(Description = "Mockolate")] + public int Mockolate_Callback() + { + var count = 0; + var svc = INotificationService.CreateMock(); + svc.Mock.Setup.Send(Mockolate.It.IsAny(), Mockolate.It.IsAny()) + .Do(() => count++); + + svc.Send("user@test.com", "Hello"); + svc.Send("user@test.com", "World"); + return count; + } + [Benchmark(Description = "Moq")] public int Moq_Callback() { var count = 0; var mock = new Moq.Mock(); - mock.Setup(x => x.Send(It.IsAny(), It.IsAny())) + mock.Setup(x => x.Send(Moq.It.IsAny(), Moq.It.IsAny())) .Callback(() => count++); var svc = mock.Object; @@ -80,12 +109,41 @@ public string TUnitMocks_CallbackWithArgs() return lastMessage; } + [Benchmark(Description = "Imposter (with args)")] + public string Imposter_CallbackWithArgs() + { + var lastMessage = ""; + var imposter = ILogger.Imposter(); + imposter.Log(Arg.Any(), Arg.Any()) + .Callback((_, msg) => lastMessage = msg); + + var instance = imposter.Instance(); + instance.Log("INFO", "Test message 1"); + instance.Log("WARN", "Test message 2"); + instance.Log("ERROR", "Test message 3"); + return lastMessage; + } + + [Benchmark(Description = "Mockolate (with args)")] + public string Mockolate_CallbackWithArgs() + { + var lastMessage = ""; + var logger = ILogger.CreateMock(); + logger.Mock.Setup.Log(Mockolate.It.IsAny(), Mockolate.It.IsAny()) + .Do((_, msg) => lastMessage = msg); + + logger.Log("INFO", "Test message 1"); + logger.Log("WARN", "Test message 2"); + logger.Log("ERROR", "Test message 3"); + return lastMessage; + } + [Benchmark(Description = "Moq (with args)")] public string Moq_CallbackWithArgs() { var lastMessage = ""; var mock = new Moq.Mock(); - mock.Setup(x => x.Log(It.IsAny(), It.IsAny())) + mock.Setup(x => x.Log(Moq.It.IsAny(), Moq.It.IsAny())) .Callback((level, msg) => lastMessage = msg); var logger = mock.Object; diff --git a/TUnit.Mocks.Benchmarks/CombinedWorkflowBenchmarks.cs b/TUnit.Mocks.Benchmarks/CombinedWorkflowBenchmarks.cs index 03ccb46249..541b5fce45 100644 --- a/TUnit.Mocks.Benchmarks/CombinedWorkflowBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/CombinedWorkflowBenchmarks.cs @@ -1,5 +1,8 @@ using BenchmarkDotNet.Attributes; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; +using Mockolate.Verify; using Moq; using NSubstitute; @@ -42,6 +45,58 @@ public void TUnitMocks_FullWorkflow() loggerMock.Log(TUnitArg.Any(), TUnitArg.Any()).WasCalled(Times.Once); } + [Benchmark(Description = "Imposter")] + public void Imposter_FullWorkflow() + { + // Create + var repoImposter = IUserRepository.Imposter(); + var loggerImposter = ILogger.Imposter(); + + // Setup + repoImposter.GetById(1).Returns(new User { Id = 1, Name = "Alice", Email = "alice@test.com" }); + repoImposter.Exists(1).Returns(true); + loggerImposter.IsEnabled(Arg.Any()).Returns(true); + + // Invoke + var repo = repoImposter.Instance(); + var logger = loggerImposter.Instance(); + var user = repo.GetById(1); + var exists = repo.Exists(1); + logger.Log("INFO", $"User {user!.Name} exists: {exists}"); + repo.Save(new User { Id = 2, Name = "Bob" }); + + // Verify + repoImposter.GetById(1).Called(Count.Once()); + repoImposter.Exists(1).Called(Count.Once()); + repoImposter.Save(Arg.Any()).Called(Count.Once()); + loggerImposter.Log(Arg.Any(), Arg.Any()).Called(Count.Once()); + } + + [Benchmark(Description = "Mockolate")] + public void Mockolate_FullWorkflow() + { + // Create + var repo = IUserRepository.CreateMock(); + var logger = ILogger.CreateMock(); + + // Setup + repo.Mock.Setup.GetById(1).Returns(new User { Id = 1, Name = "Alice", Email = "alice@test.com" }); + repo.Mock.Setup.Exists(1).Returns(true); + logger.Mock.Setup.IsEnabled(Mockolate.It.IsAny()).Returns(true); + + // Invoke + var user = repo.GetById(1); + var exists = repo.Exists(1); + logger.Log("INFO", $"User {user!.Name} exists: {exists}"); + repo.Save(new User { Id = 2, Name = "Bob" }); + + // Verify + repo.Mock.Verify.GetById(1).Once(); + repo.Mock.Verify.Exists(1).Once(); + repo.Mock.Verify.Save(Mockolate.It.IsAny()).Once(); + logger.Mock.Verify.Log(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Once(); + } + [Benchmark(Description = "Moq")] public void Moq_FullWorkflow() { @@ -52,7 +107,7 @@ public void Moq_FullWorkflow() // Setup repoMock.Setup(x => x.GetById(1)).Returns(new User { Id = 1, Name = "Alice", Email = "alice@test.com" }); repoMock.Setup(x => x.Exists(1)).Returns(true); - loggerMock.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + loggerMock.Setup(x => x.IsEnabled(Moq.It.IsAny())).Returns(true); // Invoke var repo = repoMock.Object; @@ -66,8 +121,8 @@ public void Moq_FullWorkflow() // Verify repoMock.Verify(x => x.GetById(1), Moq.Times.Once()); repoMock.Verify(x => x.Exists(1), Moq.Times.Once()); - repoMock.Verify(x => x.Save(It.IsAny()), Moq.Times.Once()); - loggerMock.Verify(x => x.Log(It.IsAny(), It.IsAny()), Moq.Times.Once()); + repoMock.Verify(x => x.Save(Moq.It.IsAny()), Moq.Times.Once()); + loggerMock.Verify(x => x.Log(Moq.It.IsAny(), Moq.It.IsAny()), Moq.Times.Once()); } [Benchmark(Description = "NSubstitute")] diff --git a/TUnit.Mocks.Benchmarks/ImposterRegistrations.cs b/TUnit.Mocks.Benchmarks/ImposterRegistrations.cs new file mode 100644 index 0000000000..aa66f78e51 --- /dev/null +++ b/TUnit.Mocks.Benchmarks/ImposterRegistrations.cs @@ -0,0 +1,7 @@ +using Imposter.Abstractions; +using TUnit.Mocks.Benchmarks; + +[assembly: GenerateImposter(typeof(INotificationService))] +[assembly: GenerateImposter(typeof(ILogger))] +[assembly: GenerateImposter(typeof(IUserRepository))] +[assembly: GenerateImposter(typeof(ICalculatorService))] diff --git a/TUnit.Mocks.Benchmarks/InvocationBenchmarks.cs b/TUnit.Mocks.Benchmarks/InvocationBenchmarks.cs index 88226a4f07..4000fe14a7 100644 --- a/TUnit.Mocks.Benchmarks/InvocationBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/InvocationBenchmarks.cs @@ -1,6 +1,7 @@ using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; using Moq; using NSubstitute; @@ -15,6 +16,9 @@ public class InvocationBenchmarks private Mock? _tunitMock; private ICalculatorService? _tunitObject; private Moq.Mock? _moqMock; + private ICalculatorServiceImposter? _imposterMock; + private ICalculatorService? _imposterObject; + private ICalculatorService? _mockolateObject; private ICalculatorService? _moqObject; private ICalculatorService? _nsubObject; private ICalculatorService? _fakeObject; @@ -28,10 +32,21 @@ public void Setup() _tunitMock.Format(TUnitArg.Any()).Returns("formatted"); _tunitObject = _tunitMock.Object; + // Imposter + _imposterMock = ICalculatorService.Imposter(); + _imposterMock.Add(Arg.Any(), Arg.Any()).Returns(42); + _imposterMock.Format(Arg.Any()).Returns("formatted"); + _imposterObject = _imposterMock.Instance(); + + // Mockolate + _mockolateObject = ICalculatorService.CreateMock(); + _mockolateObject.Mock.Setup.Add(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Returns(42); + _mockolateObject.Mock.Setup.Format(Mockolate.It.IsAny()).Returns("formatted"); + // Moq _moqMock = new Moq.Mock(); - _moqMock.Setup(x => x.Add(It.IsAny(), It.IsAny())).Returns(42); - _moqMock.Setup(x => x.Format(It.IsAny())).Returns("formatted"); + _moqMock.Setup(x => x.Add(Moq.It.IsAny(), Moq.It.IsAny())).Returns(42); + _moqMock.Setup(x => x.Format(Moq.It.IsAny())).Returns("formatted"); _moqObject = _moqMock.Object; // NSubstitute @@ -51,6 +66,18 @@ public int TUnitMocks_Invoke() return _tunitObject!.Add(1, 2); } + [Benchmark(Description = "Imposter")] + public int Imposter_Invoke() + { + return _imposterObject!.Add(1, 2); + } + + [Benchmark(Description = "Mockolate")] + public int Mockolate_Invoke() + { + return _mockolateObject!.Add(1, 2); + } + [Benchmark(Description = "Moq")] public int Moq_Invoke() { @@ -75,6 +102,18 @@ public string TUnitMocks_InvokeString() return _tunitObject!.Format(42); } + [Benchmark(Description = "Imposter (String)")] + public string Imposter_InvokeString() + { + return _imposterObject!.Format(42); + } + + [Benchmark(Description = "Mockolate (String)")] + public string Mockolate_InvokeString() + { + return _mockolateObject!.Format(42); + } + [Benchmark(Description = "Moq (String)")] public string Moq_InvokeString() { @@ -105,6 +144,30 @@ public int TUnitMocks_ManyInvocations() return sum; } + [Benchmark(Description = "Imposter (100 calls)")] + public int Imposter_ManyInvocations() + { + var sum = 0; + for (var i = 0; i < 100; i++) + { + sum += _imposterObject!.Add(i, i); + } + + return sum; + } + + [Benchmark(Description = "Mockolate (100 calls)")] + public int Mockolate_ManyInvocations() + { + var sum = 0; + for (var i = 0; i < 100; i++) + { + sum += _mockolateObject!.Add(i, i); + } + + return sum; + } + [Benchmark(Description = "Moq (100 calls)")] public int Moq_ManyInvocations() { diff --git a/TUnit.Mocks.Benchmarks/MockCreationBenchmarks.cs b/TUnit.Mocks.Benchmarks/MockCreationBenchmarks.cs index ba30d7516e..aa4d981882 100644 --- a/TUnit.Mocks.Benchmarks/MockCreationBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/MockCreationBenchmarks.cs @@ -1,8 +1,9 @@ using BenchmarkDotNet.Attributes; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; using Moq; using NSubstitute; -using TUnit.Mocks; namespace TUnit.Mocks.Benchmarks; @@ -19,6 +20,20 @@ public object TUnitMocks_CreateMock() return mock.Object; } + [Benchmark(Description = "Imposter")] + public object Imposter_CreateMock() + { + var imposter = ICalculatorService.Imposter(); + return imposter.Instance(); + } + + [Benchmark(Description = "Mockolate")] + public object Mockolate_CreateMock() + { + var sut = ICalculatorService.CreateMock(); + return sut; + } + [Benchmark(Description = "Moq")] public object Moq_CreateMock() { @@ -47,6 +62,20 @@ public object TUnitMocks_CreateMock_Repository() return mock.Object; } + [Benchmark(Description = "Imposter (Repository)")] + public object Imposter_CreateMock_Repository() + { + var imposter = IUserRepository.Imposter(); + return imposter.Instance(); + } + + [Benchmark(Description = "Mockolate (Repository)")] + public object Mockolate_CreateMock_Repository() + { + var sut = IUserRepository.CreateMock(); + return sut; + } + [Benchmark(Description = "Moq (Repository)")] public object Moq_CreateMock_Repository() { diff --git a/TUnit.Mocks.Benchmarks/SetupBenchmarks.cs b/TUnit.Mocks.Benchmarks/SetupBenchmarks.cs index d0790d661f..8ba2e88dc9 100644 --- a/TUnit.Mocks.Benchmarks/SetupBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/SetupBenchmarks.cs @@ -1,5 +1,7 @@ using BenchmarkDotNet.Attributes; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; using Moq; using NSubstitute; @@ -21,13 +23,33 @@ public object TUnitMocks_Setup() return mock.Object; } + [Benchmark(Description = "Imposter")] + public object Imposter_Setup() + { + var imposter = ICalculatorService.Imposter(); + imposter.Add(Arg.Any(), Arg.Any()).Returns(42); + imposter.Format(Arg.Any()).Returns("formatted"); + imposter.Divide(Arg.Any(), Arg.Any()).Returns(1.5); + return imposter.Instance(); + } + + [Benchmark(Description = "Mockolate")] + public object Mockolate_Setup() + { + var sut = ICalculatorService.CreateMock(); + sut.Mock.Setup.Add(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Returns(42); + sut.Mock.Setup.Format(Mockolate.It.IsAny()).Returns("formatted"); + sut.Mock.Setup.Divide(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Returns(1.5); + return sut; + } + [Benchmark(Description = "Moq")] public object Moq_Setup() { var mock = new Moq.Mock(); - mock.Setup(x => x.Add(It.IsAny(), It.IsAny())).Returns(42); - mock.Setup(x => x.Format(It.IsAny())).Returns("formatted"); - mock.Setup(x => x.Divide(It.IsAny(), It.IsAny())).Returns(1.5); + mock.Setup(x => x.Add(Moq.It.IsAny(), Moq.It.IsAny())).Returns(42); + mock.Setup(x => x.Format(Moq.It.IsAny())).Returns("formatted"); + mock.Setup(x => x.Divide(Moq.It.IsAny(), Moq.It.IsAny())).Returns(1.5); return mock.Object; } @@ -63,6 +85,30 @@ public object TUnitMocks_MultipleSetups() return mock.Object; } + [Benchmark(Description = "Imposter (Multiple)")] + public object Imposter_MultipleSetups() + { + var imposter = IUserRepository.Imposter(); + imposter.GetById(1).Returns(new User { Id = 1, Name = "Alice" }); + imposter.GetById(2).Returns(new User { Id = 2, Name = "Bob" }); + imposter.GetById(3).Returns(new User { Id = 3, Name = "Charlie" }); + imposter.Exists(Arg.Any()).Returns(true); + imposter.GetAll().Returns(new List()); + return imposter.Instance(); + } + + [Benchmark(Description = "Mockolate (Multiple)")] + public object Mockolate_MultipleSetups() + { + var sut = IUserRepository.CreateMock(); + sut.Mock.Setup.GetById(1).Returns(new User { Id = 1, Name = "Alice" }); + sut.Mock.Setup.GetById(2).Returns(new User { Id = 2, Name = "Bob" }); + sut.Mock.Setup.GetById(3).Returns(new User { Id = 3, Name = "Charlie" }); + sut.Mock.Setup.Exists(Mockolate.It.IsAny()).Returns(true); + sut.Mock.Setup.GetAll().Returns(new List()); + return sut; + } + [Benchmark(Description = "Moq (Multiple)")] public object Moq_MultipleSetups() { @@ -70,7 +116,7 @@ public object Moq_MultipleSetups() mock.Setup(x => x.GetById(1)).Returns(new User { Id = 1, Name = "Alice" }); mock.Setup(x => x.GetById(2)).Returns(new User { Id = 2, Name = "Bob" }); mock.Setup(x => x.GetById(3)).Returns(new User { Id = 3, Name = "Charlie" }); - mock.Setup(x => x.Exists(It.IsAny())).Returns(true); + mock.Setup(x => x.Exists(Moq.It.IsAny())).Returns(true); mock.Setup(x => x.GetAll()).Returns(new List()); return mock.Object; } diff --git a/TUnit.Mocks.Benchmarks/TUnit.Mocks.Benchmarks.csproj b/TUnit.Mocks.Benchmarks/TUnit.Mocks.Benchmarks.csproj index fff79507d7..91fc247610 100644 --- a/TUnit.Mocks.Benchmarks/TUnit.Mocks.Benchmarks.csproj +++ b/TUnit.Mocks.Benchmarks/TUnit.Mocks.Benchmarks.csproj @@ -12,6 +12,8 @@ + + diff --git a/TUnit.Mocks.Benchmarks/VerificationBenchmarks.cs b/TUnit.Mocks.Benchmarks/VerificationBenchmarks.cs index 324f0fc3d6..065f3229c6 100644 --- a/TUnit.Mocks.Benchmarks/VerificationBenchmarks.cs +++ b/TUnit.Mocks.Benchmarks/VerificationBenchmarks.cs @@ -1,5 +1,8 @@ using BenchmarkDotNet.Attributes; using FakeItEasy; +using Imposter.Abstractions; +using Mockolate; +using Mockolate.Verify; using Moq; using NSubstitute; @@ -23,16 +26,39 @@ public void TUnitMocks_Verify() mock.Add(TUnitArg.Any(), TUnitArg.Any()).WasCalled(Times.Exactly(2)); } + [Benchmark(Description = "Imposter")] + public void Imposter_Verify() + { + var imposter = ICalculatorService.Imposter(); + imposter.Add(Arg.Any(), Arg.Any()).Returns(42); + var calc = imposter.Instance(); + calc.Add(1, 2); + calc.Add(3, 4); + + imposter.Add(Arg.Any(), Arg.Any()).Called(Count.Exactly(2)); + } + + [Benchmark(Description = "Mockolate")] + public void Mockolate_Verify() + { + var sut = ICalculatorService.CreateMock(); + sut.Mock.Setup.Add(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Returns(42); + sut.Add(1, 2); + sut.Add(3, 4); + + sut.Mock.Verify.Add(Mockolate.It.IsAny(), Mockolate.It.IsAny()).Exactly(2); + } + [Benchmark(Description = "Moq")] public void Moq_Verify() { var mock = new Moq.Mock(); - mock.Setup(x => x.Add(It.IsAny(), It.IsAny())).Returns(42); + mock.Setup(x => x.Add(Moq.It.IsAny(), Moq.It.IsAny())).Returns(42); var calc = mock.Object; calc.Add(1, 2); calc.Add(3, 4); - mock.Verify(x => x.Add(It.IsAny(), It.IsAny()), Moq.Times.Exactly(2)); + mock.Verify(x => x.Add(Moq.It.IsAny(), Moq.It.IsAny()), Moq.Times.Exactly(2)); } [Benchmark(Description = "NSubstitute")] @@ -64,11 +90,26 @@ public void TUnitMocks_VerifyNever() mock.Format(TUnitArg.Any()).WasNeverCalled(); } + [Benchmark(Description = "Imposter (Never)")] + public void Imposter_VerifyNever() + { + var imposter = ICalculatorService.Imposter(); + _ = imposter.Instance(); + imposter.Format(Arg.Any()).Called(Count.Never()); + } + + [Benchmark(Description = "Mockolate (Never)")] + public void Mockolate_VerifyNever() + { + var sut = ICalculatorService.CreateMock(); + sut.Mock.Verify.Format(Mockolate.It.IsAny()).Never(); + } + [Benchmark(Description = "Moq (Never)")] public void Moq_VerifyNever() { var mock = new Moq.Mock(); - mock.Verify(x => x.Format(It.IsAny()), Moq.Times.Never()); + mock.Verify(x => x.Format(Moq.It.IsAny()), Moq.Times.Never()); } [Benchmark(Description = "NSubstitute (Never)")] @@ -103,12 +144,47 @@ public void TUnitMocks_VerifyMultiple() mock.Save(TUnitArg.Any()).WasCalled(Times.Once); } + [Benchmark(Description = "Imposter (Multiple)")] + public void Imposter_VerifyMultiple() + { + var imposter = IUserRepository.Imposter(); + imposter.GetById(Arg.Any()).Returns(new User { Id = 1, Name = "Test" }); + imposter.Exists(Arg.Any()).Returns(true); + + var repo = imposter.Instance(); + repo.GetById(1); + repo.GetById(2); + repo.Exists(1); + repo.Save(new User { Id = 3, Name = "New" }); + + imposter.GetById(Arg.Any()).Called(Count.Exactly(2)); + imposter.Exists(Arg.Any()).Called(Count.Once()); + imposter.Save(Arg.Any()).Called(Count.Once()); + } + + [Benchmark(Description = "Mockolate (Multiple)")] + public void Mockolate_VerifyMultiple() + { + var sut = IUserRepository.CreateMock(); + sut.Mock.Setup.GetById(Mockolate.It.IsAny()).Returns(new User { Id = 1, Name = "Test" }); + sut.Mock.Setup.Exists(Mockolate.It.IsAny()).Returns(true); + + sut.GetById(1); + sut.GetById(2); + sut.Exists(1); + sut.Save(new User { Id = 3, Name = "New" }); + + sut.Mock.Verify.GetById(Mockolate.It.IsAny()).Exactly(2); + sut.Mock.Verify.Exists(Mockolate.It.IsAny()).Once(); + sut.Mock.Verify.Save(Mockolate.It.IsAny()).Once(); + } + [Benchmark(Description = "Moq (Multiple)")] public void Moq_VerifyMultiple() { var mock = new Moq.Mock(); - mock.Setup(x => x.GetById(It.IsAny())).Returns(new User { Id = 1, Name = "Test" }); - mock.Setup(x => x.Exists(It.IsAny())).Returns(true); + mock.Setup(x => x.GetById(Moq.It.IsAny())).Returns(new User { Id = 1, Name = "Test" }); + mock.Setup(x => x.Exists(Moq.It.IsAny())).Returns(true); var repo = mock.Object; repo.GetById(1); @@ -116,9 +192,9 @@ public void Moq_VerifyMultiple() repo.Exists(1); repo.Save(new User { Id = 3, Name = "New" }); - mock.Verify(x => x.GetById(It.IsAny()), Moq.Times.Exactly(2)); - mock.Verify(x => x.Exists(It.IsAny()), Moq.Times.Once()); - mock.Verify(x => x.Save(It.IsAny()), Moq.Times.Once()); + mock.Verify(x => x.GetById(Moq.It.IsAny()), Moq.Times.Exactly(2)); + mock.Verify(x => x.Exists(Moq.It.IsAny()), Moq.Times.Once()); + mock.Verify(x => x.Save(Moq.It.IsAny()), Moq.Times.Once()); } [Benchmark(Description = "NSubstitute (Multiple)")]