From 680451fd015be94ab1e1c37f98133503d06678a2 Mon Sep 17 00:00:00 2001 From: marcchevaldonne Date: Sun, 3 Mar 2024 02:04:25 +0100 Subject: [PATCH] :tada: mocks and moq --- Sources/Samples.sln | 48 +++++++ Sources/mocks/Model/IEmailService.cs | 6 + Sources/mocks/Model/INounoursStore.cs | 8 ++ Sources/mocks/Model/Model.csproj | 9 ++ Sources/mocks/Model/Nounours.cs | 11 ++ Sources/mocks/Model/NounoursManager.cs | 34 +++++ Sources/mocks/UTests/GlobalUsings.cs | 1 + Sources/mocks/UTests/MockEmailService.cs | 27 ++++ Sources/mocks/UTests/MockNounoursStore.cs | 43 ++++++ Sources/mocks/UTests/UTests.csproj | 29 ++++ Sources/mocks/UTests/UnitTests.cs | 81 +++++++++++ Sources/mocks/UTests2/GlobalUsings.cs | 1 + Sources/mocks/UTests2/MockEmailSevice.cs | 23 ++++ Sources/mocks/UTests2/MockNounoursStore.cs | 26 ++++ Sources/mocks/UTests2/UTests2.csproj | 29 ++++ Sources/mocks/UTests2/UnitTests.cs | 152 +++++++++++++++++++++ Sources/mocks/UTests3/GlobalUsings.cs | 1 + Sources/mocks/UTests3/UTests3.csproj | 30 ++++ Sources/mocks/UTests3/UnitTests.cs | 107 +++++++++++++++ 19 files changed, 666 insertions(+) create mode 100644 Sources/Samples.sln create mode 100644 Sources/mocks/Model/IEmailService.cs create mode 100644 Sources/mocks/Model/INounoursStore.cs create mode 100644 Sources/mocks/Model/Model.csproj create mode 100644 Sources/mocks/Model/Nounours.cs create mode 100644 Sources/mocks/Model/NounoursManager.cs create mode 100644 Sources/mocks/UTests/GlobalUsings.cs create mode 100644 Sources/mocks/UTests/MockEmailService.cs create mode 100644 Sources/mocks/UTests/MockNounoursStore.cs create mode 100644 Sources/mocks/UTests/UTests.csproj create mode 100644 Sources/mocks/UTests/UnitTests.cs create mode 100644 Sources/mocks/UTests2/GlobalUsings.cs create mode 100644 Sources/mocks/UTests2/MockEmailSevice.cs create mode 100644 Sources/mocks/UTests2/MockNounoursStore.cs create mode 100644 Sources/mocks/UTests2/UTests2.csproj create mode 100644 Sources/mocks/UTests2/UnitTests.cs create mode 100644 Sources/mocks/UTests3/GlobalUsings.cs create mode 100644 Sources/mocks/UTests3/UTests3.csproj create mode 100644 Sources/mocks/UTests3/UnitTests.cs diff --git a/Sources/Samples.sln b/Sources/Samples.sln new file mode 100644 index 0000000..e750180 --- /dev/null +++ b/Sources/Samples.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mocks", "mocks", "{98474E06-6ECA-468B-9693-30AA3708FA0A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "mocks\Model\Model.csproj", "{12FE8D34-611E-4AB9-93BE-C74A7D947405}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UTests", "mocks\UTests\UTests.csproj", "{93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UTests2", "mocks\UTests2\UTests2.csproj", "{991E0C4E-48DE-4699-BCF7-070029923143}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UTests3", "mocks\UTests3\UTests3.csproj", "{A9FE0291-58A1-42E5-9768-34B3A33F4116}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12FE8D34-611E-4AB9-93BE-C74A7D947405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12FE8D34-611E-4AB9-93BE-C74A7D947405}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12FE8D34-611E-4AB9-93BE-C74A7D947405}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12FE8D34-611E-4AB9-93BE-C74A7D947405}.Release|Any CPU.Build.0 = Release|Any CPU + {93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4}.Release|Any CPU.Build.0 = Release|Any CPU + {991E0C4E-48DE-4699-BCF7-070029923143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {991E0C4E-48DE-4699-BCF7-070029923143}.Debug|Any CPU.Build.0 = Debug|Any CPU + {991E0C4E-48DE-4699-BCF7-070029923143}.Release|Any CPU.ActiveCfg = Release|Any CPU + {991E0C4E-48DE-4699-BCF7-070029923143}.Release|Any CPU.Build.0 = Release|Any CPU + {A9FE0291-58A1-42E5-9768-34B3A33F4116}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9FE0291-58A1-42E5-9768-34B3A33F4116}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9FE0291-58A1-42E5-9768-34B3A33F4116}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9FE0291-58A1-42E5-9768-34B3A33F4116}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {12FE8D34-611E-4AB9-93BE-C74A7D947405} = {98474E06-6ECA-468B-9693-30AA3708FA0A} + {93AA2CF9-A29A-41D2-9ECC-87BE7BD121A4} = {98474E06-6ECA-468B-9693-30AA3708FA0A} + {991E0C4E-48DE-4699-BCF7-070029923143} = {98474E06-6ECA-468B-9693-30AA3708FA0A} + {A9FE0291-58A1-42E5-9768-34B3A33F4116} = {98474E06-6ECA-468B-9693-30AA3708FA0A} + EndGlobalSection +EndGlobal diff --git a/Sources/mocks/Model/IEmailService.cs b/Sources/mocks/Model/IEmailService.cs new file mode 100644 index 0000000..9b72625 --- /dev/null +++ b/Sources/mocks/Model/IEmailService.cs @@ -0,0 +1,6 @@ +namespace Model; + +public interface IEmailService +{ + Task AskConfirmation(Nounours nounours); +} \ No newline at end of file diff --git a/Sources/mocks/Model/INounoursStore.cs b/Sources/mocks/Model/INounoursStore.cs new file mode 100644 index 0000000..d3e9ccb --- /dev/null +++ b/Sources/mocks/Model/INounoursStore.cs @@ -0,0 +1,8 @@ +namespace Model; + +public interface INounoursStore +{ + Task Add(Nounours nounours); + + Task Remove(Nounours nounours); +} \ No newline at end of file diff --git a/Sources/mocks/Model/Model.csproj b/Sources/mocks/Model/Model.csproj new file mode 100644 index 0000000..bb23fb7 --- /dev/null +++ b/Sources/mocks/Model/Model.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Sources/mocks/Model/Nounours.cs b/Sources/mocks/Model/Nounours.cs new file mode 100644 index 0000000..ba25fdf --- /dev/null +++ b/Sources/mocks/Model/Nounours.cs @@ -0,0 +1,11 @@ +namespace Model; + +public class Nounours +{ + public string Email { get; private set; } + + public Nounours(string email) + { + Email = email; + } +} \ No newline at end of file diff --git a/Sources/mocks/Model/NounoursManager.cs b/Sources/mocks/Model/NounoursManager.cs new file mode 100644 index 0000000..de09814 --- /dev/null +++ b/Sources/mocks/Model/NounoursManager.cs @@ -0,0 +1,34 @@ +namespace Model; + +public class NounoursManager +{ + private INounoursStore nounoursStore; + + private IEmailService emailService; + + public NounoursManager(INounoursStore nounoursStore, IEmailService emailService) + { + this.nounoursStore = nounoursStore; + this.emailService = emailService; + } + + public async Task AddNewNounours(Nounours nounours) + { + var result = await nounoursStore.Add(nounours); + + if(result == null) return false; + + try + { + await emailService.AskConfirmation(nounours); + } + catch + { + bool removed = await nounoursStore.Remove(nounours); + if(!removed) throw new Exception("this should not happen..."); + return false; + } + + return true; + } +} diff --git a/Sources/mocks/UTests/GlobalUsings.cs b/Sources/mocks/UTests/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/Sources/mocks/UTests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/Sources/mocks/UTests/MockEmailService.cs b/Sources/mocks/UTests/MockEmailService.cs new file mode 100644 index 0000000..1db5286 --- /dev/null +++ b/Sources/mocks/UTests/MockEmailService.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using Model; + +public class MockEmailService : IEmailService +{ + public MockEmailService(bool sendEmail) + { + SendEmail = sendEmail; + } + + public string? EmailSentTo {get; private set; } + + private bool SendEmail {get; set;} + + public Task AskConfirmation(Nounours nounours) + { + if(SendEmail) + { + EmailSentTo = nounours.Email; + return Task.CompletedTask; + } + else + { + throw new Exception(); + } + } +} diff --git a/Sources/mocks/UTests/MockNounoursStore.cs b/Sources/mocks/UTests/MockNounoursStore.cs new file mode 100644 index 0000000..6cd9ee4 --- /dev/null +++ b/Sources/mocks/UTests/MockNounoursStore.cs @@ -0,0 +1,43 @@ +using Model; + +namespace UTests; + +public class MockNounoursStore : INounoursStore +{ + public MockNounoursStore(bool canAdd, bool canRemove) + { + CanAdd = canAdd; + CanRemove = canRemove; + } + + private bool CanAdd {get; set; } + private bool CanRemove {get; set; } + + public Nounours? AddedNounours { get; private set; } + + public Task Add(Nounours nounours) + { + if(CanAdd) + { + AddedNounours = nounours; + return Task.FromResult(nounours); + } + else + { + return Task.FromResult(null); + } + } + + public Task Remove(Nounours nounours) + { + if(CanRemove) + { + AddedNounours = null; + return Task.FromResult(true); + } + else + { + return Task.FromResult(false); + } + } +} \ No newline at end of file diff --git a/Sources/mocks/UTests/UTests.csproj b/Sources/mocks/UTests/UTests.csproj new file mode 100644 index 0000000..7da462a --- /dev/null +++ b/Sources/mocks/UTests/UTests.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Sources/mocks/UTests/UnitTests.cs b/Sources/mocks/UTests/UnitTests.cs new file mode 100644 index 0000000..92bf8ee --- /dev/null +++ b/Sources/mocks/UTests/UnitTests.cs @@ -0,0 +1,81 @@ +using Model; + +namespace UTests; + +public class UnitTests +{ + [Fact] + public async Task Test_AddNewNounours_Success() + { + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore(canAdd: true, canRemove: true); + MockEmailService emailService = new MockEmailService(sendEmail: true); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + var nounours = new Nounours("chucky.chucky@hell.com"); + + //execute the method to test + await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.Equal(nounours.Email, nounoursStore.AddedNounours?.Email); + Assert.Equal(nounours.Email, emailService.EmailSentTo); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_AlreadyExists() + { + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore(canAdd: false, canRemove: true); + MockEmailService emailService = new MockEmailService(sendEmail: true); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + var nounours = new Nounours("chucky.chucky@hell.com"); + + //execute the method to test + await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.Null(nounoursStore.AddedNounours); + Assert.Null(emailService.EmailSentTo); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_BadEmail() + { + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore(canAdd: true, canRemove: true); + MockEmailService emailService = new MockEmailService(sendEmail: false); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + var nounours = new Nounours("chucky.chucky@hell.com"); + + //execute the method to test + await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.Null(nounoursStore.AddedNounours); + Assert.Null(emailService.EmailSentTo); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_ExceptionWhenRemoving() + { + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore(canAdd: true, canRemove: false); + MockEmailService emailService = new MockEmailService(sendEmail: false); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + var nounours = new Nounours("chucky.chucky@hell.com"); + + //assert it throws an exception + _ = await Assert.ThrowsAsync(async () => await mgr.AddNewNounours(nounours)); + + //assert the interactions went correctly + Assert.Equal(nounours.Email, nounoursStore.AddedNounours?.Email); + } +} \ No newline at end of file diff --git a/Sources/mocks/UTests2/GlobalUsings.cs b/Sources/mocks/UTests2/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/Sources/mocks/UTests2/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/Sources/mocks/UTests2/MockEmailSevice.cs b/Sources/mocks/UTests2/MockEmailSevice.cs new file mode 100644 index 0000000..4a169e0 --- /dev/null +++ b/Sources/mocks/UTests2/MockEmailSevice.cs @@ -0,0 +1,23 @@ +using Model; + +namespace UTests2; + +public class MockEmailService : IEmailService +{ + public Func AskConfirmation {get; set;} = null!; + Task IEmailService.AskConfirmation(Nounours nounours) + { + if(AskConfirmation != null) + { + if(AskConfirmation(nounours)) + { + return Task.CompletedTask; + } + else + { + throw new Exception(); + } + } + throw new Exception(); + } +} \ No newline at end of file diff --git a/Sources/mocks/UTests2/MockNounoursStore.cs b/Sources/mocks/UTests2/MockNounoursStore.cs new file mode 100644 index 0000000..ec693a3 --- /dev/null +++ b/Sources/mocks/UTests2/MockNounoursStore.cs @@ -0,0 +1,26 @@ +using Model; + +namespace UTests2; + +public class MockNounoursStore : INounoursStore +{ + public Func Add {get; set;} = null!; + public Func Remove {get; set;} = null!; + Task INounoursStore.Add(Nounours nounours) + { + if (Add != null) + { + return Task.FromResult(Add(nounours)); + } + return Task.FromResult(null); + } + + Task INounoursStore.Remove(Nounours nounours) + { + if(Remove != null) + { + return Task.FromResult(Remove(nounours)); + } + return Task.FromResult(false); + } +} \ No newline at end of file diff --git a/Sources/mocks/UTests2/UTests2.csproj b/Sources/mocks/UTests2/UTests2.csproj new file mode 100644 index 0000000..7da462a --- /dev/null +++ b/Sources/mocks/UTests2/UTests2.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Sources/mocks/UTests2/UnitTests.cs b/Sources/mocks/UTests2/UnitTests.cs new file mode 100644 index 0000000..5b57add --- /dev/null +++ b/Sources/mocks/UTests2/UnitTests.cs @@ -0,0 +1,152 @@ +using Model; + +namespace UTests2; + +public class UnitTests +{ + [Fact] + public async Task Test_AddNewNounours_Success() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore + { + Add = n => + { + Assert.Equal(nounours.Email, n?.Email); + return n; + } + }; + + MockEmailService emailService = new MockEmailService + { + AskConfirmation = n => + { + Assert.Equal("chucky.chucky@hell.com", n.Email); + return true; + } + }; + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.True(result); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_AlreadyExists() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore + { + Add = n => + { + Assert.Equal(nounours.Email, n?.Email); + return null; + }, + Remove = n => + { + Assert.Equal(nounours.Email, n?.Email); + return true; + } + }; + MockEmailService emailService = new MockEmailService + { + AskConfirmation = n => + { + Assert.Equal("chucky.chucky@hell.com", n.Email); + return true; + } + }; + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.False(result); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_BadEmail() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore + { + Add = n => + { + Assert.Equal(nounours.Email, n?.Email); + return n; + }, + Remove = n => + { + Assert.Equal(nounours.Email, n?.Email); + return true; + } + }; + MockEmailService emailService = new MockEmailService + { + AskConfirmation = n => + { + Assert.Equal(nounours.Email, n.Email); + return false; + } + }; + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.False(result); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_ExceptionWhenRemoving() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + + //setup the mocks + MockNounoursStore nounoursStore = new MockNounoursStore + { + Add = n => + { + Assert.Equal(nounours.Email, n?.Email); + return n; + }, + Remove = n => + { + Assert.Equal(nounours.Email, n?.Email); + return false; + } + }; + MockEmailService emailService = new MockEmailService + { + AskConfirmation = n => + { + Assert.Equal("chucky.chucky@hell.com", n.Email); + return false; + } + }; + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(nounoursStore, emailService); + + + //assert it throws an exception + _ = await Assert.ThrowsAsync(async () => await mgr.AddNewNounours(nounours)); + } +} \ No newline at end of file diff --git a/Sources/mocks/UTests3/GlobalUsings.cs b/Sources/mocks/UTests3/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/Sources/mocks/UTests3/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/Sources/mocks/UTests3/UTests3.csproj b/Sources/mocks/UTests3/UTests3.csproj new file mode 100644 index 0000000..5e10b82 --- /dev/null +++ b/Sources/mocks/UTests3/UTests3.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Sources/mocks/UTests3/UnitTests.cs b/Sources/mocks/UTests3/UnitTests.cs new file mode 100644 index 0000000..eb5808c --- /dev/null +++ b/Sources/mocks/UTests3/UnitTests.cs @@ -0,0 +1,107 @@ +using Model; +using Moq; + +namespace UTests3; + +public class UnitTests +{ + [Fact] + public async Task Test_AddNewNounours_Success() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + //setup the mocks + var mockStore = new Mock(); + mockStore.Setup(store => store.Add(nounours)).ReturnsAsync(() => nounours); + + var mockEmailService = new Mock(); + //mockEmailService.Setup(service => service.AskConfirmation(nounours)); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(mockStore.Object, mockEmailService.Object); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.True(result); + mockStore.Verify(store => store.Add(nounours), Times.Once); + mockStore.Verify(store => store.Remove(nounours), Times.Never); + mockEmailService.Verify(store => store.AskConfirmation(nounours), Times.Once); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_AlreadyExists() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + //setup the mocks + var mockStore = new Mock(); + mockStore.Setup(store => store.Add(nounours)).ReturnsAsync(() => null); + //mockStore.Setup(store => store.Remove(nounours)).ReturnsAsync(true); + + var mockEmailService = new Mock(); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(mockStore.Object, mockEmailService.Object); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.False(result); + mockStore.Verify(store => store.Add(nounours), Times.Once); + mockStore.VerifyNoOtherCalls(); + mockEmailService.VerifyNoOtherCalls(); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_BadEmail() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + //setup the mocks + var mockStore = new Mock(); + mockStore.Setup(store => store.Add(nounours)).ReturnsAsync(() => nounours); + mockStore.Setup(store => store.Remove(nounours)).ReturnsAsync(true); + + var mockEmailService = new Mock(); + mockEmailService.Setup(service => service.AskConfirmation(nounours)).Throws(); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(mockStore.Object, mockEmailService.Object); + + //execute the method to test + bool result = await mgr.AddNewNounours(nounours); + + //assert the interactions went correctly + Assert.False(result); + mockStore.Verify(store => store.Add(nounours), Times.Once); + mockStore.Verify(store => store.Remove(nounours), Times.Once); + mockStore.VerifyNoOtherCalls(); + mockEmailService.Verify(service => service.AskConfirmation(nounours), Times.Once); + mockEmailService.VerifyNoOtherCalls(); + } + + [Fact] + public async Task Test_AddNewNounours_Fail_ExceptionWhenRemoving() + { + var nounours = new Nounours("chucky.chucky@hell.com"); + //setup the mocks + var mockStore = new Mock(); + mockStore.Setup(store => store.Add(nounours)).ReturnsAsync(() => nounours); + mockStore.Setup(store => store.Remove(nounours)).ReturnsAsync(false); + + var mockEmailService = new Mock(); + mockEmailService.Setup(service => service.AskConfirmation(nounours)).Throws(); + + //setup the subject-under-test + NounoursManager mgr = new NounoursManager(mockStore.Object, mockEmailService.Object); + + //assert it throws an exception + _ = await Assert.ThrowsAsync(async () => await mgr.AddNewNounours(nounours)); + + mockStore.Verify(store => store.Add(nounours), Times.Once); + mockStore.Verify(store => store.Remove(nounours), Times.Once); + mockStore.VerifyNoOtherCalls(); + mockEmailService.Verify(service => service.AskConfirmation(nounours), Times.Once); + mockEmailService.VerifyNoOtherCalls(); + } +} \ No newline at end of file