diff --git a/GettingStartedWithInterfacesAndMocks/DiceGame/Entities/Die.cs b/GettingStartedWithInterfacesAndMocks/DiceGame/Entities/Die.cs new file mode 100644 index 0000000..224e568 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocks/DiceGame/Entities/Die.cs @@ -0,0 +1,22 @@ +using GettingStartedWithInterfacesAndMocks.DiceGame.Extensions; +using GettingStartedWithInterfacesAndMocks.DiceGame.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GettingStartedWithInterfacesAndMocks.DiceGame.Entities +{ + public class Die : IDie + { + public Die() + { + Roll(); + } + + public int Value { get; private set; } + public void Roll() + { + Value = DieRoller.GetD6Roll(); + } + } +} diff --git a/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieExtensions.cs b/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieExtensions.cs new file mode 100644 index 0000000..f7dfa14 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieExtensions.cs @@ -0,0 +1,25 @@ +using GettingStartedWithInterfacesAndMocks.DiceGame.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GettingStartedWithInterfacesAndMocks.DiceGame.Extensions +{ + public static class DieExtensions + { + public static bool IsValidRoll(this IEnumerable dice) + { + return dice.Count() == 5; + } + + public static bool IsFiveOfAKind(this IEnumerable dice) + { + if (!dice.IsValidRoll()) + { + throw new ArgumentException("A valid roll must contain exactly five dice"); + } + + return dice.GroupBy(d => d.Value).Count() == 1; + } + } +} diff --git a/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieRoller.cs b/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieRoller.cs new file mode 100644 index 0000000..161edb6 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocks/DiceGame/Extensions/DieRoller.cs @@ -0,0 +1,22 @@ +using GettingStartedWithInterfacesAndMocks.DiceGame.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GettingStartedWithInterfacesAndMocks.DiceGame.Extensions +{ + public static class DieRoller + { + static Random _rng = new Random(); + + public static int GetD6Roll() + { + return _rng.Next(1, 7); + } + + public static IEnumerable GetGameRoll() + { + return Enumerable.Range(1, 5).Select(_ => new Die()); + } + } +} diff --git a/GettingStartedWithInterfacesAndMocks/DiceGame/Interfaces/IDie.cs b/GettingStartedWithInterfacesAndMocks/DiceGame/Interfaces/IDie.cs new file mode 100644 index 0000000..5d6b6e6 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocks/DiceGame/Interfaces/IDie.cs @@ -0,0 +1,10 @@ +using System; + +namespace GettingStartedWithInterfacesAndMocks.DiceGame.Interfaces +{ + public interface IDie + { + void Roll(); + int Value { get; } + } +} diff --git a/GettingStartedWithInterfacesAndMocks/GettingStartedWithInterfacesAndMocks.csproj b/GettingStartedWithInterfacesAndMocks/GettingStartedWithInterfacesAndMocks.csproj new file mode 100644 index 0000000..cb63190 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocks/GettingStartedWithInterfacesAndMocks.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/GettingStartedWithInterfacesAndMocksTests/DieExtensionsTests.cs b/GettingStartedWithInterfacesAndMocksTests/DieExtensionsTests.cs new file mode 100644 index 0000000..0d559a3 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocksTests/DieExtensionsTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using GettingStartedWithInterfacesAndMocks.DiceGame.Entities; +using GettingStartedWithInterfacesAndMocks.DiceGame.Extensions; +using GettingStartedWithInterfacesAndMocksTests.TestingMocks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GettingStartedWithInterfacesAndMocksTests +{ + [TestClass] + public class DieExtensionsTests + { + [TestMethod] + public void IsValidRoll_RollHas4Dice_ReturnsFalse() + { + var dice = Enumerable.Range(1, 4).Select(_ => new Die()); + + dice.IsValidRoll().Should().BeFalse(because: "A valid game roll has five dice"); + } + + [TestMethod] + public void IsValidRoll_RollHas5Dice_ReturnsTrue() + { + var dice = Enumerable.Range(1, 5).Select(_ => new Die()); + + dice.IsValidRoll().Should().BeTrue(because: "A valid game roll has five dice"); + } + + [TestMethod] + public void IsFiveOfAKind_AllDiceAreTheSame_ReturnsTrue() + { + var dice = Enumerable.Range(1, 5).Select(_ => new SettableD6(5)); + + dice.IsFiveOfAKind().Should().BeTrue(); + } + + [TestMethod] + public void IsFiveOfAKind_AllDiceAreNotTheSame_ReturnsFalse() + { + var dice = new[]{ + new SettableD6(1), + new SettableD6(3), + new SettableD6(4), + new SettableD6(1), + new SettableD6(1) + }; + + dice.IsFiveOfAKind().Should().BeFalse(); + } + + [TestMethod] + public void IsFiveOfAKind_NotAValidRoll_ThrowsArgumentException() + { + var dice = new[]{ + new SettableD6(2), + new SettableD6(3) + }; + + Action validateRoll = () => dice.IsFiveOfAKind(); + + validateRoll.Should().Throw(); + } + } +} diff --git a/GettingStartedWithInterfacesAndMocksTests/GettingStartedWithInterfacesAndMocksTests.csproj b/GettingStartedWithInterfacesAndMocksTests/GettingStartedWithInterfacesAndMocksTests.csproj new file mode 100644 index 0000000..7abe736 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocksTests/GettingStartedWithInterfacesAndMocksTests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/GettingStartedWithInterfacesAndMocksTests/TestingMocks/SettableD6.cs b/GettingStartedWithInterfacesAndMocksTests/TestingMocks/SettableD6.cs new file mode 100644 index 0000000..18b0939 --- /dev/null +++ b/GettingStartedWithInterfacesAndMocksTests/TestingMocks/SettableD6.cs @@ -0,0 +1,22 @@ +using GettingStartedWithInterfacesAndMocks.DiceGame.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GettingStartedWithInterfacesAndMocksTests.TestingMocks +{ + public class SettableD6 : IDie + { + public SettableD6(int value) + { + Value = value; + } + + public int Value { get; set; } + + public void Roll() + { + //Does nothing + } + } +} diff --git a/TestingPracticesWithExamples.sln b/TestingPracticesWithExamples.sln index 4dfbcaf..3df040c 100644 --- a/TestingPracticesWithExamples.sln +++ b/TestingPracticesWithExamples.sln @@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29609.76 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "GettingStarted\GettingStarted.csproj", "{AAB66502-A5F2-40D8-BCB4-F7EDB0923640}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InterfacesAndMocks", "InterfacesAndMocks", "{42786B9F-B4BA-4D56-8104-D359C5E0E917}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStartedWithInterfacesAndMocks", "GettingStartedWithInterfacesAndMocks\GettingStartedWithInterfacesAndMocks.csproj", "{22E941F6-26EC-47CE-84A7-0456BF6EF2A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStartedWithInterfacesAndMocksTests", "GettingStartedWithInterfacesAndMocksTests\GettingStartedWithInterfacesAndMocksTests.csproj", "{7BE9A85F-1902-4448-8023-9DC5EF821EE7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,14 +15,22 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AAB66502-A5F2-40D8-BCB4-F7EDB0923640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AAB66502-A5F2-40D8-BCB4-F7EDB0923640}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AAB66502-A5F2-40D8-BCB4-F7EDB0923640}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AAB66502-A5F2-40D8-BCB4-F7EDB0923640}.Release|Any CPU.Build.0 = Release|Any CPU + {22E941F6-26EC-47CE-84A7-0456BF6EF2A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22E941F6-26EC-47CE-84A7-0456BF6EF2A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22E941F6-26EC-47CE-84A7-0456BF6EF2A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22E941F6-26EC-47CE-84A7-0456BF6EF2A1}.Release|Any CPU.Build.0 = Release|Any CPU + {7BE9A85F-1902-4448-8023-9DC5EF821EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BE9A85F-1902-4448-8023-9DC5EF821EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BE9A85F-1902-4448-8023-9DC5EF821EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BE9A85F-1902-4448-8023-9DC5EF821EE7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {22E941F6-26EC-47CE-84A7-0456BF6EF2A1} = {42786B9F-B4BA-4D56-8104-D359C5E0E917} + {7BE9A85F-1902-4448-8023-9DC5EF821EE7} = {42786B9F-B4BA-4D56-8104-D359C5E0E917} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BA44BE1E-A335-4CCF-9ACF-D80E199328B3} EndGlobalSection