├── .gitignore ├── LICENSE ├── Module1 ├── Easy │ ├── Calculator.cs │ ├── CalculatorTests.cs │ └── Program.cs └── Hard │ └── Program.cs ├── Module2 ├── Easy │ ├── DateTimeWrapper.cs │ ├── IDatabase.cs │ ├── IDateTimeWrapper.cs │ ├── IPrinter.cs │ ├── PrintInvoiceCommand.cs │ ├── PrintInvoiceCommandTests.cs │ ├── PrintInvoiceCommandTestsWithAutoMoqer.cs │ └── Printer.cs ├── Hard │ ├── PrintInvoiceCommand.cs │ └── Printer.cs └── Shared │ ├── Database.cs │ └── Invoice.cs ├── Module3 ├── Easy │ ├── IInvoiceWriter.cs │ ├── InvoiceWriter.cs │ ├── InvoiceWriterTests.cs │ └── PrintInvoiceCommand.cs ├── Extras │ ├── PrintInvoiceCommandFactory.cs │ ├── Program1.cs │ ├── Program2.cs │ ├── Program3.cs │ └── Program4.cs ├── Hard │ ├── InvoiceWriter.cs │ └── PrintInvoiceCommand.cs └── Shared │ ├── Database.cs │ ├── IDatabase.cs │ ├── IPageLayout.cs │ ├── IPrinter.cs │ ├── Invoice.cs │ ├── PageLayout.cs │ └── Printer.cs ├── Module4 ├── Easy │ ├── PrintInvoiceCommand.cs │ └── PrintInvoiceCommandTests.cs ├── Hard │ └── PrintInvoiceCommand.cs └── Shared │ ├── Container.cs │ ├── IDatabase.cs │ ├── IIdentityService.cs │ ├── IInvoiceWriter.cs │ ├── ISession.cs │ ├── IdentityService.cs │ ├── Invoice.cs │ ├── InvoiceStatus.cs │ ├── Login.cs │ ├── Session.cs │ └── User.cs ├── Module5 ├── Easy │ ├── ISecurity.cs │ ├── PrintInvoiceCommand.cs │ ├── PrintInvoiceCommandTests.cs │ ├── Program.cs │ ├── Security.cs │ └── SingletonTests.cs ├── Hard │ ├── PrintInvoiceCommand.cs │ └── Security.cs └── Shared │ ├── Database.cs │ ├── IDatabase.cs │ ├── IInvoiceWriter.cs │ ├── Invoice.cs │ ├── InvoiceStatus.cs │ ├── InvoiceWriter.cs │ └── UserNotAuthorizedException.cs ├── Module6 ├── Easy │ ├── EmailInvoiceCommand.cs │ ├── EmailInvoiceCommandTests.cs │ ├── PrintInvoiceCommand.cs │ └── PrintInvoiceCommandTests.cs ├── Hard │ ├── PrintOrEmailInvoiceCommand.cs │ └── PrintOrEmailInvoiceCommandTests.cs └── Shared │ ├── Database.cs │ ├── EmailAddressIsBlankException.cs │ ├── IDatabase.cs │ ├── IEmailValidator.cs │ ├── IInvoiceEmailer.cs │ ├── IInvoiceWriter.cs │ ├── ISecurity.cs │ ├── Invoice.cs │ ├── InvoiceWriter.cs │ ├── Security.cs │ └── UserNotAuthorizedException.cs ├── README.md ├── TestableCodeDemos.csproj └── TestableCodeDemos.sln /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | *.ncrunchproject 117 | *.ncrunchsolution 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | *.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Matthew Renze 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Module1/Easy/Calculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module1.Easy 6 | { 7 | public class Calculator 8 | { 9 | public decimal GetTotal(decimal parts, decimal service, decimal discount) 10 | { 11 | return parts + service - discount; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Module1/Easy/CalculatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace TestableCodeDemos.Module1.Easy 7 | { 8 | [TestFixture] 9 | public class CalculatorTests 10 | { 11 | private Calculator _calculator; 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | _calculator = new Calculator(); 17 | } 18 | 19 | [Test] 20 | public void TestGetTotalShouldReturnTotalPrice() 21 | { 22 | var result = _calculator.GetTotal(1.00m, 2.00m, 0.50m); 23 | 24 | Assert.That(result, Is.EqualTo(2.50m)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Module1/Easy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module1.Easy 6 | { 7 | class Program 8 | { 9 | static void Main_Easy(string[] args) 10 | { 11 | var parts = decimal.Parse(args[0]); 12 | 13 | var service = decimal.Parse(args[1]); 14 | 15 | var discount = decimal.Parse(args[2]); 16 | 17 | var calculator = new Calculator(); 18 | 19 | var total = calculator.GetTotal(parts, service, discount); 20 | 21 | Console.WriteLine("Total Price: $" + total); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Module1/Hard/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module1.Hard 6 | { 7 | class Program 8 | { 9 | static void Main_Hard(string[] args) 10 | { 11 | var parts = decimal.Parse(args[0]); 12 | 13 | var service = decimal.Parse(args[1]); 14 | 15 | var discount = decimal.Parse(args[2]); 16 | 17 | var total = parts + service - discount; 18 | 19 | Console.WriteLine("Total Price: $" + total); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Module2/Easy/DateTimeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TestableCodeDemos.Module2.Easy 8 | { 9 | public class DateTimeWrapper : IDateTimeWrapper 10 | { 11 | public DateTime GetNow() 12 | { 13 | return DateTime.Now; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Module2/Easy/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module2.Shared; 5 | 6 | namespace TestableCodeDemos.Module2.Easy 7 | { 8 | public interface IDatabase 9 | { 10 | Invoice GetInvoice(int invoiceId); 11 | } 12 | } -------------------------------------------------------------------------------- /Module2/Easy/IDateTimeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module2.Easy 6 | { 7 | public interface IDateTimeWrapper 8 | { 9 | DateTime GetNow(); 10 | } 11 | } -------------------------------------------------------------------------------- /Module2/Easy/IPrinter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module2.Easy 6 | { 7 | public interface IPrinter 8 | { 9 | void WriteLine(string text); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Module2/Easy/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module2.Easy 6 | { 7 | public class PrintInvoiceCommand 8 | { 9 | private readonly IDatabase _database; 10 | private readonly IPrinter _printer; 11 | private readonly IDateTimeWrapper _dateTime; 12 | 13 | public PrintInvoiceCommand( 14 | IDatabase database, 15 | IPrinter printer, 16 | IDateTimeWrapper dateTime) 17 | { 18 | _database = database; 19 | _printer = printer; 20 | _dateTime = dateTime; 21 | } 22 | 23 | public void Execute(int invoiceId) 24 | { 25 | var invoice = _database.GetInvoice(invoiceId); 26 | 27 | _printer.WriteLine("Invoice ID: " + invoice.Id); 28 | 29 | _printer.WriteLine("Total: $" + invoice.Total); 30 | 31 | var dateTime = _dateTime.GetNow(); 32 | 33 | _printer.WriteLine("Printed: " + dateTime.ToShortDateString()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Module2/Easy/PrintInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | using NUnit.Framework; 4 | using TestableCodeDemos.Module2.Shared; 5 | 6 | namespace TestableCodeDemos.Module2.Easy 7 | { 8 | [TestFixture] 9 | public class PrintInvoiceCommandTests 10 | { 11 | private PrintInvoiceCommand _command; 12 | private Mock _mockDatabase; 13 | private Mock _mockPrinter; 14 | private Mock _mockDateTime; 15 | private Invoice _invoice; 16 | 17 | private const int InvoiceId = 1; 18 | private const decimal Total = 1.23m; 19 | private static readonly DateTime Date = new DateTime(2001, 2, 3); 20 | 21 | [SetUp] 22 | public void SetUp() 23 | { 24 | _invoice = new Invoice() 25 | { 26 | Id = InvoiceId, 27 | Total = Total 28 | }; 29 | 30 | _mockDatabase = new Mock(); 31 | _mockPrinter = new Mock(); 32 | _mockDateTime = new Mock(); 33 | 34 | _mockDatabase 35 | .Setup(p => p.GetInvoice(InvoiceId)) 36 | .Returns(_invoice); 37 | 38 | _mockDateTime 39 | .Setup(p => p.GetNow()) 40 | .Returns(Date); 41 | 42 | _command = new PrintInvoiceCommand( 43 | _mockDatabase.Object, 44 | _mockPrinter.Object, 45 | _mockDateTime.Object); 46 | } 47 | 48 | [Test] 49 | public void TestExecuteShouldPrintInvoiceNumber() 50 | { 51 | _command.Execute(InvoiceId); 52 | 53 | _mockPrinter 54 | .Verify(p => p.WriteLine("Invoice ID: 1"), 55 | Times.Once); 56 | } 57 | 58 | [Test] 59 | public void TestExecuteShouldPrintTotalPrice() 60 | { 61 | _command.Execute(InvoiceId); 62 | 63 | _mockPrinter 64 | .Verify(p => p.WriteLine("Total: $1.23"), 65 | Times.Once); 66 | } 67 | 68 | [Test] 69 | public void TestExecuteShouldPrintTodaysDate() 70 | { 71 | _command.Execute(InvoiceId); 72 | 73 | _mockPrinter 74 | .Verify(p => p.WriteLine("Printed: 2/3/2001"), 75 | Times.Once); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Module2/Easy/PrintInvoiceCommandTestsWithAutoMoqer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Moq; 5 | using Moq.AutoMock; 6 | using NUnit.Framework; 7 | using TestableCodeDemos.Module2.Shared; 8 | 9 | namespace TestableCodeDemos.Module2.Easy 10 | { 11 | [TestFixture] 12 | public class PrintInvoiceCommandTestsWithAutoMocker 13 | { 14 | private PrintInvoiceCommand _command; 15 | private AutoMocker _mocker; 16 | private Invoice _invoice; 17 | 18 | private const int InvoiceId = 1; 19 | private const decimal Total = 4.50m; 20 | private static readonly DateTime Date = new DateTime(2001, 2, 3); 21 | 22 | [SetUp] 23 | public void SetUp() 24 | { 25 | _invoice = new Invoice() 26 | { 27 | Id = InvoiceId, 28 | Total = Total 29 | }; 30 | 31 | _mocker = new AutoMocker(); 32 | 33 | _mocker.GetMock() 34 | .Setup(p => p.GetInvoice(InvoiceId)) 35 | .Returns(_invoice); 36 | 37 | _mocker.GetMock() 38 | .Setup(p => p.GetNow()) 39 | .Returns(Date); 40 | 41 | _command = _mocker.CreateInstance(); 42 | } 43 | 44 | [Test] 45 | [TestCase("Invoice ID: 1")] 46 | [TestCase("Total: $4.50")] 47 | [TestCase("Printed: 2/3/2001")] 48 | public void TestExecuteShouldPrintLine(string line) 49 | { 50 | _command.Execute(InvoiceId); 51 | 52 | _mocker.GetMock() 53 | .Verify(p => p.WriteLine(line), 54 | Times.Once); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Module2/Easy/Printer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module2.Easy 6 | { 7 | public class Printer : IPrinter 8 | { 9 | public void WriteLine(string text) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Module2/Hard/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module2.Shared; 5 | 6 | namespace TestableCodeDemos.Module2.Hard 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | public void Execute(int invoiceId) 11 | { 12 | var database = new Database(); 13 | 14 | var invoice = database.GetInvoice(invoiceId); 15 | 16 | Printer.WriteLine("Invoice ID: " + invoice.Id); 17 | 18 | Printer.WriteLine("Total: $" + invoice.Total); 19 | 20 | var dateTime = DateTime.Now; 21 | 22 | Printer.WriteLine("Printed: " + dateTime.ToShortDateString()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Module2/Hard/Printer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TestableCodeDemos.Module2.Hard 8 | { 9 | public class Printer 10 | { 11 | public static void WriteLine(string text) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Module2/Shared/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module2.Easy; 5 | 6 | namespace TestableCodeDemos.Module2.Shared 7 | { 8 | public class Database : IDatabase 9 | { 10 | public Invoice GetInvoice(int invoiceId) 11 | { 12 | throw new System.NotImplementedException(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Module2/Shared/Invoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module2.Shared 6 | { 7 | public class Invoice 8 | { 9 | public int Id { get; set; } 10 | 11 | public decimal Total { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module3/Easy/IInvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using TestableCodeDemos.Module3.Shared; 2 | 3 | namespace TestableCodeDemos.Module3.Easy 4 | { 5 | public interface IInvoiceWriter 6 | { 7 | void Write(Invoice invoice); 8 | } 9 | } -------------------------------------------------------------------------------- /Module3/Easy/InvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TestableCodeDemos.Module3.Shared; 7 | 8 | namespace TestableCodeDemos.Module3.Easy 9 | { 10 | public class InvoiceWriter : IInvoiceWriter 11 | { 12 | private readonly IPrinter _printer; 13 | private readonly IPageLayout _layout; 14 | 15 | public InvoiceWriter( 16 | IPrinter printer, 17 | IPageLayout layout) 18 | { 19 | _printer = printer; 20 | _layout = layout; 21 | } 22 | 23 | public void Write(Invoice invoice) 24 | { 25 | _printer.SetPageLayout(_layout); 26 | 27 | if (invoice.IsOverdue) 28 | _printer.SetInkColor("Red"); 29 | 30 | _printer.WriteLine("Invoice ID: " + invoice.Id); 31 | 32 | // Remaining print statements would go here 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Module3/Easy/InvoiceWriterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Moq; 7 | using Moq.AutoMock; 8 | using NUnit.Framework; 9 | using TestableCodeDemos.Module3.Shared; 10 | 11 | namespace TestableCodeDemos.Module3.Easy 12 | { 13 | [TestFixture] 14 | public class InvoiceWriterTests 15 | { 16 | private InvoiceWriter _writer; 17 | private AutoMocker _mocker; 18 | private Invoice _invoice; 19 | 20 | [SetUp] 21 | public void SetUp() 22 | { 23 | _invoice = new Invoice() 24 | { 25 | Id = 1, 26 | IsOverdue = false 27 | }; 28 | 29 | _mocker = new AutoMocker(); 30 | 31 | _writer = _mocker.CreateInstance(); 32 | } 33 | 34 | [Test] 35 | public void TestWriteShouldSetPageLayout() 36 | { 37 | _writer.Write(_invoice); 38 | 39 | var layout = _mocker 40 | .GetMock().Object; 41 | 42 | _mocker.GetMock() 43 | .Verify(p => p.SetPageLayout(layout), 44 | Times.Once); 45 | } 46 | 47 | [Test] 48 | public void TestWriteShouldPrintOverdueInvoiceInRed() 49 | { 50 | _invoice.IsOverdue = true; 51 | 52 | _writer.Write(_invoice); 53 | 54 | _mocker.GetMock() 55 | .Verify(p => p.SetInkColor("Red"), 56 | Times.Once); 57 | } 58 | 59 | [Test] 60 | public void TestWriteShouldPrintOnTimeInvoiceInDefaultColor() 61 | { 62 | _writer.Write(_invoice); 63 | 64 | _mocker.GetMock() 65 | .Verify(p => p.SetInkColor(It.IsAny()), 66 | Times.Never); 67 | } 68 | 69 | [Test] 70 | [TestCase("Invoice ID: 1")] 71 | // Remaining test cases would go here 72 | public void TestWriteShouldPrintInvoiceNumber(string line) 73 | { 74 | _writer.Write(_invoice); 75 | 76 | _mocker.GetMock() 77 | .Verify(p => p.WriteLine(line), 78 | Times.Once()); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Module3/Easy/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TestableCodeDemos.Module3.Shared; 7 | 8 | namespace TestableCodeDemos.Module3.Easy 9 | { 10 | public class PrintInvoiceCommand 11 | { 12 | private readonly IDatabase _database; 13 | private readonly IInvoiceWriter _writer; 14 | 15 | public PrintInvoiceCommand( 16 | IDatabase database, 17 | IInvoiceWriter writer) 18 | { 19 | _database = database; 20 | _writer = writer; 21 | } 22 | 23 | public void Execute(int invoiceId) 24 | { 25 | var invoice = _database.GetInvoice(invoiceId); 26 | 27 | _writer.Write(invoice); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Module3/Extras/PrintInvoiceCommandFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module3.Easy; 5 | using TestableCodeDemos.Module3.Shared; 6 | 7 | namespace TestableCodeDemos.Module3.Extras 8 | { 9 | public class PrintInvoiceCommandFactory 10 | { 11 | public PrintInvoiceCommand Create() 12 | { 13 | var command = new PrintInvoiceCommand( 14 | new Database(), 15 | new InvoiceWriter( 16 | new Printer(), 17 | new PageLayout())); 18 | 19 | return command; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Module3/Extras/Program1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module3.Easy; 5 | using TestableCodeDemos.Module3.Shared; 6 | 7 | namespace TestableCodeDemos.Module3.Extras 8 | { 9 | public class Program 10 | { 11 | static void Main_(string[] args) 12 | { 13 | var invoiceId = int.Parse(args[0]); 14 | 15 | var command = new PrintInvoiceCommand( 16 | new Database(), 17 | new InvoiceWriter( 18 | new Printer(), 19 | new PageLayout())); 20 | 21 | command.Execute(invoiceId); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Module3/Extras/Program2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Extras 6 | { 7 | public class Program2 8 | { 9 | static void Main_(string[] args) 10 | { 11 | var invoiceId = int.Parse(args[0]); 12 | 13 | var factory = new PrintInvoiceCommandFactory(); 14 | 15 | var command = factory.Create(); 16 | 17 | command.Execute(invoiceId); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Module3/Extras/Program3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Ninject; 5 | using TestableCodeDemos.Module3.Easy; 6 | using TestableCodeDemos.Module3.Shared; 7 | 8 | namespace TestableCodeDemos.Module3.Extras 9 | { 10 | public class Program3 11 | { 12 | static void Main_(string[] args) 13 | { 14 | var container = new StandardKernel(); 15 | 16 | container.Bind() 17 | .To(); 18 | 19 | container.Bind() 20 | .To(); 21 | 22 | container.Bind() 23 | .To(); 24 | 25 | container.Bind() 26 | .To(); 27 | 28 | var invoiceId = int.Parse(args[0]); 29 | 30 | var command = container.Get(); 31 | 32 | command.Execute(invoiceId); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Module3/Extras/Program4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Ninject; 5 | using Ninject.Extensions.Conventions; 6 | using TestableCodeDemos.Module3.Easy; 7 | 8 | namespace TestableCodeDemos.Module3.Extras 9 | { 10 | public class Program4 11 | { 12 | static void Main_(string[] args) 13 | { 14 | var container = new StandardKernel(); 15 | 16 | container.Bind(p => 17 | { 18 | p.FromThisAssembly() 19 | .SelectAllClasses() 20 | .BindDefaultInterface(); 21 | }); 22 | 23 | var invoiceId = int.Parse(args[0]); 24 | 25 | var command = container.Get(); 26 | 27 | command.Execute(invoiceId); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Module3/Hard/InvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TestableCodeDemos.Module3.Shared; 7 | 8 | namespace TestableCodeDemos.Module3.Hard 9 | { 10 | public class InvoiceWriter 11 | { 12 | private readonly IPrinter _printer; 13 | private readonly Invoice _invoice; 14 | 15 | public InvoiceWriter( 16 | IPrinter printer, 17 | Invoice invoice) 18 | { 19 | _printer = printer; 20 | _invoice = invoice; 21 | 22 | _printer.SetPageLayout(new PageLayout()); 23 | 24 | if (_invoice.IsOverdue) 25 | _printer.SetInkColor("Red"); 26 | } 27 | 28 | public void Write() 29 | { 30 | _printer.WriteLine("Invoice ID: " + _invoice.Id); 31 | 32 | // Remaining print statements would go here 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Module3/Hard/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TestableCodeDemos.Module3.Shared; 7 | 8 | namespace TestableCodeDemos.Module3.Hard 9 | { 10 | public class PrintInvoiceCommand 11 | { 12 | private readonly IDatabase _database; 13 | private readonly IPrinter _printer; 14 | 15 | public PrintInvoiceCommand( 16 | IDatabase database, 17 | IPrinter printer) 18 | { 19 | _database = database; 20 | _printer = printer; 21 | } 22 | 23 | public void Execute(int invoiceId) 24 | { 25 | var invoice = _database.GetInvoice(invoiceId); 26 | 27 | var writer = new InvoiceWriter(_printer, invoice); 28 | 29 | writer.Write(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Module3/Shared/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public class Database : IDatabase 8 | { 9 | public Invoice GetInvoice(int invoiceId) 10 | { 11 | throw new System.NotImplementedException(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Module3/Shared/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public interface IDatabase 8 | { 9 | Invoice GetInvoice(int invoiceId); 10 | } 11 | } -------------------------------------------------------------------------------- /Module3/Shared/IPageLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public interface IPageLayout 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Module3/Shared/IPrinter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public interface IPrinter 8 | { 9 | void SetPageLayout(IPageLayout layout); 10 | 11 | void SetInkColor(string color); 12 | 13 | void WriteLine(string text); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Module3/Shared/Invoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public class Invoice 8 | { 9 | public int Id { get; set; } 10 | 11 | // Remaining invoices properties would go here 12 | 13 | public bool IsOverdue { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Module3/Shared/PageLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public class PageLayout : IPageLayout 8 | { 9 | // Page layout logic would go here 10 | } 11 | } -------------------------------------------------------------------------------- /Module3/Shared/Printer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module3.Shared 6 | { 7 | public class Printer :IPrinter 8 | { 9 | public void SetInkColor(string red) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public void SetPageLayout(IPageLayout logic) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | public void WriteLine(string text) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Module4/Easy/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module4.Shared; 5 | 6 | namespace TestableCodeDemos.Module4.Easy 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | private readonly IDatabase _database; 11 | private readonly IInvoiceWriter _writer; 12 | private readonly IIdentityService _identity; 13 | 14 | public PrintInvoiceCommand( 15 | IDatabase database, 16 | IInvoiceWriter writer, 17 | IIdentityService identity) 18 | { 19 | _database = database; 20 | _writer = writer; 21 | _identity = identity; 22 | } 23 | 24 | public void Execute(int invoiceId) 25 | { 26 | var invoice = _database.GetInvoice(invoiceId); 27 | 28 | _writer.Write(invoice); 29 | 30 | invoice.LastPrintedBy = _identity.GetUserName(); 31 | 32 | _database.Save(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Module4/Easy/PrintInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Moq; 5 | using Moq.AutoMock; 6 | using NUnit.Framework; 7 | using TestableCodeDemos.Module4.Shared; 8 | 9 | namespace TestableCodeDemos.Module4.Easy 10 | { 11 | [TestFixture] 12 | public class PrintInvoiceCommandTests 13 | { 14 | private PrintInvoiceCommand _command; 15 | private AutoMocker _mocker; 16 | private Invoice _invoice; 17 | 18 | private const int InvoiceId = 1; 19 | private const string UserName = "mrenze"; 20 | 21 | [SetUp] 22 | public void SetUp() 23 | { 24 | _invoice = new Invoice(); 25 | 26 | _mocker = new AutoMocker(); 27 | 28 | _mocker.GetMock() 29 | .Setup(p => p.GetInvoice(InvoiceId)) 30 | .Returns(_invoice); 31 | 32 | _mocker.GetMock() 33 | .Setup(p => p.GetUserName()) 34 | .Returns(UserName); 35 | 36 | _command = _mocker.CreateInstance(); 37 | } 38 | 39 | [Test] 40 | public void TestExecuteShouldPrintInvoice() 41 | { 42 | _command.Execute(InvoiceId); 43 | 44 | _mocker.GetMock() 45 | .Verify(p => p.Write(_invoice), 46 | Times.Once); 47 | } 48 | 49 | [Test] 50 | public void TestExecuteShouldLastPrintedByToCurrentUserName() 51 | { 52 | _command.Execute(InvoiceId); 53 | 54 | Assert.That(_invoice.LastPrintedBy, 55 | Is.EqualTo(UserName)); 56 | } 57 | 58 | [Test] 59 | public void TestExecuteShouldSaveChangesToDatabase() 60 | { 61 | _command.Execute(InvoiceId); 62 | 63 | _mocker.GetMock() 64 | .Verify(p => p.Save(), 65 | Times.Once); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Module4/Hard/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module4.Shared; 5 | 6 | namespace TestableCodeDemos.Module4.Hard 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | private readonly Container _container; 11 | 12 | public PrintInvoiceCommand( 13 | Container container) 14 | { 15 | _container = container; 16 | } 17 | 18 | public void Execute(int invoiceId) 19 | { 20 | var invoice = _container 21 | .Get() 22 | .GetInvoice(invoiceId); 23 | 24 | _container.Get() 25 | .Write(invoice); 26 | 27 | invoice.LastPrintedBy = _container 28 | .Get() 29 | .GetLogin() 30 | .GetUser() 31 | .GetUserName(); 32 | 33 | _container 34 | .Get() 35 | .Save(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Module4/Shared/Container.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class Container 8 | { 9 | public T Get() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Module4/Shared/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public interface IDatabase 8 | { 9 | Invoice GetInvoice(int invoiceId); 10 | 11 | void Save(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module4/Shared/IIdentityService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public interface IIdentityService 8 | { 9 | string GetUserName(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Module4/Shared/IInvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TestableCodeDemos.Module4.Shared 8 | { 9 | public interface IInvoiceWriter 10 | { 11 | void Write(Invoice invoice); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module4/Shared/ISession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public interface ISession 8 | { 9 | Login GetLogin(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Module4/Shared/IdentityService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class IdentityService : IIdentityService 8 | { 9 | public string GetUserName() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Module4/Shared/Invoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class Invoice 8 | { 9 | // Remaining invoice fields would go here 10 | 11 | public InvoiceStatus Status { get; set; } 12 | 13 | public string LastPrintedBy { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Module4/Shared/InvoiceStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public enum InvoiceStatus 8 | { 9 | Open, 10 | Closed, 11 | Cancelled 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module4/Shared/Login.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class Login 8 | { 9 | private readonly User _user; 10 | 11 | public Login(User user) 12 | { 13 | _user = user; 14 | } 15 | 16 | public User GetUser() 17 | { 18 | return _user; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Module4/Shared/Session.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class Session : ISession 8 | { 9 | private readonly Login _login; 10 | 11 | public Session(Login login) 12 | { 13 | _login = login; 14 | } 15 | 16 | public Login GetLogin() 17 | { 18 | return _login; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Module4/Shared/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module4.Shared 6 | { 7 | public class User 8 | { 9 | private readonly string _userName; 10 | 11 | public User(string userName) 12 | { 13 | _userName = userName; 14 | } 15 | 16 | public string GetUserName() 17 | { 18 | return _userName; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Module5/Easy/ISecurity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Easy 6 | { 7 | public interface ISecurity 8 | { 9 | string GetUserName(); 10 | 11 | bool IsAdmin(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module5/Easy/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module5.Shared; 5 | 6 | namespace TestableCodeDemos.Module5.Easy 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | private readonly IDatabase _database; 11 | private readonly ISecurity _security; 12 | private readonly IInvoiceWriter _writer; 13 | 14 | public PrintInvoiceCommand( 15 | IDatabase database, 16 | ISecurity security, 17 | IInvoiceWriter writer) 18 | { 19 | _database = database; 20 | _security = security; 21 | _writer = writer; 22 | } 23 | 24 | public void Execute(int invoiceId) 25 | { 26 | var invoice = _database.GetInvoice(invoiceId); 27 | 28 | if (!_security.IsAdmin()) 29 | throw new UserNotAuthorizedException(); 30 | 31 | _writer.Print(invoice); 32 | 33 | invoice.LastPrintedBy = _security.GetUserName(); 34 | 35 | _database.Save(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Module5/Easy/PrintInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Moq; 5 | using Moq.AutoMock; 6 | using NUnit.Framework; 7 | using TestableCodeDemos.Module5.Shared; 8 | 9 | namespace TestableCodeDemos.Module5.Easy 10 | { 11 | [TestFixture] 12 | public class PrintInvoiceCommandTests 13 | { 14 | private PrintInvoiceCommand _command; 15 | private AutoMocker _mocker; 16 | private Invoice _invoice; 17 | 18 | private const int InvoiceId = 1; 19 | private const string UserName = "mrenze"; 20 | 21 | [SetUp] 22 | public void SetUp() 23 | { 24 | _invoice = new Invoice(); 25 | 26 | _mocker = new AutoMocker(); 27 | 28 | _mocker.GetMock(). 29 | Setup(p => p.GetInvoice(InvoiceId)) 30 | .Returns(_invoice); 31 | 32 | _mocker.GetMock() 33 | .Setup(p => p.IsAdmin()) 34 | .Returns(true); 35 | 36 | _mocker.GetMock() 37 | .Setup(p => p.GetUserName()) 38 | .Returns(UserName); 39 | 40 | _command = _mocker.CreateInstance(); 41 | } 42 | 43 | [Test] 44 | public void TestExecuteShouldThrowExceptionIfUserIsNotAdmin() 45 | { 46 | _mocker.GetMock() 47 | .Setup(p => p.IsAdmin()) 48 | .Returns(false); 49 | 50 | Assert.That(() => _command.Execute(InvoiceId), 51 | Throws.TypeOf()); 52 | } 53 | 54 | [Test] 55 | public void TestExecuteShouldPrintInvoice() 56 | { 57 | _command.Execute(InvoiceId); 58 | 59 | _mocker.GetMock() 60 | .Verify(p => p.Print(_invoice), 61 | Times.Once); 62 | } 63 | 64 | [Test] 65 | public void TestExecuteShouldSetLastPrintedByToCurrentUser() 66 | { 67 | _command.Execute(InvoiceId); 68 | 69 | Assert.That(_invoice.LastPrintedBy, 70 | Is.EqualTo(UserName)); 71 | } 72 | 73 | [Test] 74 | public void TestExecuteShouldSaveChangesToDatabase() 75 | { 76 | _command.Execute(InvoiceId); 77 | 78 | _mocker.GetMock() 79 | .Verify(p => p.Save(), 80 | Times.Once); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Module5/Easy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Ninject; 5 | using Ninject.Extensions.Conventions; 6 | 7 | namespace TestableCodeDemos.Module5.Easy 8 | { 9 | public class Program 10 | { 11 | static void Main_(string[] args) 12 | { 13 | var invoiceId = int.Parse(args[0]); 14 | 15 | var container = new StandardKernel(); 16 | 17 | container.Bind(p => 18 | { 19 | p.FromThisAssembly() 20 | .SelectAllClasses() 21 | .BindDefaultInterface(); 22 | }); 23 | 24 | container.Bind() 25 | .To() 26 | .InSingletonScope(); 27 | 28 | var command = container.Get(); 29 | 30 | command.Execute(invoiceId); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Module5/Easy/Security.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Easy 6 | { 7 | public class Security : ISecurity 8 | { 9 | private string _userName; 10 | private bool _isAdmin; 11 | 12 | public void SetUser(string userName, bool isAdmin) 13 | { 14 | _userName = userName; 15 | 16 | _isAdmin = isAdmin; 17 | } 18 | 19 | public string GetUserName() 20 | { 21 | return _userName; 22 | } 23 | 24 | public bool IsAdmin() 25 | { 26 | return _isAdmin; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Module5/Easy/SingletonTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Ninject; 5 | using NUnit.Framework; 6 | 7 | namespace TestableCodeDemos.Module5.Easy 8 | { 9 | [TestFixture] 10 | public class SingletonTests 11 | { 12 | [Test] 13 | public void TestTransientScopeReturnsDifferentInstance() 14 | { 15 | var container = new StandardKernel(); 16 | 17 | container.Bind() 18 | .To(); 19 | 20 | var security1 = container.Get(); 21 | 22 | var security2 = container.Get(); 23 | 24 | Assert.That(security1, 25 | Is.Not.SameAs(security2)); 26 | } 27 | 28 | [Test] 29 | public void TestSingletonReturnsSameInstance() 30 | { 31 | var container = new StandardKernel(); 32 | 33 | container.Bind() 34 | .To() 35 | .InSingletonScope(); 36 | 37 | var security1 = container.Get(); 38 | 39 | var security2 = container.Get(); 40 | 41 | Assert.That(security1, 42 | Is.SameAs(security2)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Module5/Hard/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module5.Shared; 5 | 6 | namespace TestableCodeDemos.Module5.Hard 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | private readonly IDatabase _database; 11 | private readonly IInvoiceWriter _writer; 12 | 13 | public PrintInvoiceCommand( 14 | IDatabase database, 15 | IInvoiceWriter writer) 16 | { 17 | _database = database; 18 | _writer = writer; 19 | } 20 | 21 | public void Execute(int invoiceId) 22 | { 23 | var invoice = _database.GetInvoice(invoiceId); 24 | 25 | var security = Security.GetInstance(); 26 | 27 | if (!security.IsAdmin()) 28 | throw new UserNotAuthorizedException(); 29 | 30 | _writer.Print(invoice); 31 | 32 | invoice.LastPrintedBy = security.GetUserName(); 33 | 34 | _database.Save(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Module5/Hard/Security.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Hard 6 | { 7 | public class Security 8 | { 9 | private static Security _instance; 10 | private string _userName; 11 | private bool _isAdmin; 12 | 13 | public static Security GetInstance() 14 | { 15 | if (_instance == null) 16 | _instance = new Security(); 17 | 18 | return _instance; 19 | } 20 | 21 | private Security() { } 22 | 23 | public void SetUser(string userName, bool isAdmin) 24 | { 25 | _userName = userName; 26 | 27 | _isAdmin = isAdmin; 28 | } 29 | 30 | public string GetUserName() 31 | { 32 | return _userName; 33 | } 34 | 35 | public bool IsAdmin() 36 | { 37 | return _isAdmin; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Module5/Shared/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public class Database : IDatabase 8 | { 9 | public Invoice GetInvoice(int invoiceId) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public void Save() 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Module5/Shared/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public interface IDatabase 8 | { 9 | Invoice GetInvoice(int invoiceId); 10 | 11 | void Save(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module5/Shared/IInvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public interface IInvoiceWriter 8 | { 9 | void Print(Invoice invoice); 10 | } 11 | } -------------------------------------------------------------------------------- /Module5/Shared/Invoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public class Invoice 8 | { 9 | public string EmailAddress { get; set; } 10 | 11 | public string LastPrintedBy { get; set; } 12 | 13 | // Other invoice fields and methods would go here 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Module5/Shared/InvoiceStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public enum InvoiceStatus 8 | { 9 | Open, 10 | Closed, 11 | Canceled 12 | } 13 | } -------------------------------------------------------------------------------- /Module5/Shared/InvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public class InvoiceWriter : IInvoiceWriter 8 | { 9 | public void Print(Invoice invoice) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Module5/Shared/UserNotAuthorizedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module5.Shared 6 | { 7 | public class UserNotAuthorizedException : Exception 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Module6/Easy/EmailInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TestableCodeDemos.Module6.Shared; 7 | 8 | namespace TestableCodeDemos.Module6.Easy 9 | { 10 | public class EmailInvoiceCommand 11 | { 12 | private readonly IDatabase _database; 13 | private readonly IInvoiceEmailer _emailer; 14 | 15 | public EmailInvoiceCommand( 16 | IDatabase database, 17 | IInvoiceEmailer emailer) 18 | { 19 | _database = database; 20 | _emailer = emailer; 21 | } 22 | 23 | public void Execute(int invoiceId) 24 | { 25 | var invoice = _database.GetInvoice(invoiceId); 26 | 27 | if (invoice.EmailAddress == string.Empty) 28 | throw new EmailAddressIsBlankException(); 29 | 30 | _emailer.Email(invoice); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Module6/Easy/EmailInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Moq; 7 | using Moq.AutoMock; 8 | using NUnit.Framework; 9 | using TestableCodeDemos.Module6.Shared; 10 | 11 | namespace TestableCodeDemos.Module6.Easy 12 | { 13 | [TestFixture] 14 | public class EmailInvoiceCommandTests 15 | { 16 | private EmailInvoiceCommand _command; 17 | private AutoMocker _mocker; 18 | private Invoice _invoice; 19 | 20 | private const int InvoiceId = 1; 21 | private const string EmailAddress = "email@test.com"; 22 | 23 | [SetUp] 24 | public void SetUp() 25 | { 26 | _invoice = new Invoice() 27 | { 28 | EmailAddress = EmailAddress 29 | }; 30 | 31 | _mocker = new AutoMocker(); 32 | 33 | _mocker.GetMock() 34 | .Setup(p => p.GetInvoice(InvoiceId)) 35 | .Returns(_invoice); 36 | 37 | _command = _mocker.CreateInstance(); 38 | } 39 | 40 | [Test] 41 | public void TestExecuteForInvoiceWithNoEmailAddressShouldThrowException() 42 | { 43 | _invoice.EmailAddress = string.Empty; 44 | 45 | Assert.That(() => _command.Execute(InvoiceId), 46 | Throws.TypeOf()); 47 | } 48 | 49 | [Test] 50 | public void TestExecuteShouldEmailInvoice() 51 | { 52 | _command.Execute(InvoiceId); 53 | 54 | _mocker.GetMock() 55 | .Verify(p => p.Email(_invoice), 56 | Times.Once); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Module6/Easy/PrintInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module6.Shared; 5 | 6 | namespace TestableCodeDemos.Module6.Easy 7 | { 8 | public class PrintInvoiceCommand 9 | { 10 | private readonly IDatabase _database; 11 | private readonly ISecurity _security; 12 | private readonly IInvoiceWriter _processor; 13 | 14 | public PrintInvoiceCommand( 15 | IDatabase database, 16 | ISecurity security, 17 | IInvoiceWriter processor) 18 | { 19 | _database = database; 20 | _security = security; 21 | _processor = processor; 22 | } 23 | 24 | public void Execute(int invoiceId) 25 | { 26 | var invoice = _database.GetInvoice(invoiceId); 27 | 28 | if (!_security.IsAdmin()) 29 | throw new UserNotAuthorizedException(); 30 | 31 | _processor.Print(invoice); 32 | 33 | invoice.LastPrintedBy = _security.GetUserName(); 34 | 35 | _database.Save(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Module6/Easy/PrintInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Moq; 5 | using Moq.AutoMock; 6 | using NUnit.Framework; 7 | using TestableCodeDemos.Module6.Shared; 8 | 9 | namespace TestableCodeDemos.Module6.Easy 10 | { 11 | [TestFixture] 12 | public class PrintInvoiceCommandTests 13 | { 14 | private PrintInvoiceCommand _command; 15 | private AutoMocker _mocker; 16 | private Invoice _invoice; 17 | 18 | private const int InvoiceId = 1; 19 | private const string UserName = "mrenze"; 20 | 21 | [SetUp] 22 | public void SetUp() 23 | { 24 | _invoice = new Invoice(); 25 | 26 | _mocker = new AutoMocker(); 27 | 28 | _mocker.GetMock(). 29 | Setup(p => p.GetInvoice(InvoiceId)) 30 | .Returns(_invoice); 31 | 32 | _mocker.GetMock() 33 | .Setup(p => p.IsAdmin()) 34 | .Returns(true); 35 | 36 | _mocker.GetMock() 37 | .Setup(p => p.GetUserName()) 38 | .Returns(UserName); 39 | 40 | _command = _mocker.CreateInstance(); 41 | } 42 | 43 | [Test] 44 | public void TestExecuteShouldThrowExceptionIfUserIsNotAdmin() 45 | { 46 | _mocker.GetMock() 47 | .Setup(p => p.IsAdmin()) 48 | .Returns(false); 49 | 50 | Assert.That(() => _command.Execute(InvoiceId), 51 | Throws.TypeOf()); 52 | } 53 | 54 | [Test] 55 | public void TestExecuteShouldPrintInvoice() 56 | { 57 | _command.Execute(InvoiceId); 58 | 59 | _mocker.GetMock() 60 | .Verify(p => p.Print(_invoice), 61 | Times.Once); 62 | } 63 | 64 | [Test] 65 | public void TestExecuteShouldSetLastPrintedByToCurrentUser() 66 | { 67 | _command.Execute(InvoiceId); 68 | 69 | Assert.That(_invoice.LastPrintedBy, 70 | Is.EqualTo("mrenze")); 71 | } 72 | 73 | [Test] 74 | public void TestExecuteShouldSaveChangesToDatabase() 75 | { 76 | _command.Execute(InvoiceId); 77 | 78 | _mocker.GetMock() 79 | .Verify(p => p.Save(), 80 | Times.Once); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Module6/Hard/PrintOrEmailInvoiceCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using TestableCodeDemos.Module6.Shared; 5 | 6 | namespace TestableCodeDemos.Module6.Hard 7 | { 8 | public class PrintOrEmailInvoiceCommand 9 | { 10 | private readonly IDatabase _database; 11 | private readonly ISecurity _security; 12 | private readonly IInvoiceEmailer _emailer; 13 | private readonly IInvoiceWriter _writer; 14 | 15 | public PrintOrEmailInvoiceCommand( 16 | IDatabase database, 17 | ISecurity security, 18 | IInvoiceEmailer emailer, 19 | IInvoiceWriter writer) 20 | { 21 | _database = database; 22 | _security = security; 23 | _emailer = emailer; 24 | _writer = writer; 25 | } 26 | 27 | public void Execute(int invoiceId, bool shouldEmail) 28 | { 29 | var invoice = _database.GetInvoice(invoiceId); 30 | 31 | if (shouldEmail) 32 | { 33 | if (invoice.EmailAddress == string.Empty) 34 | throw new EmailAddressIsBlankException(); 35 | 36 | _emailer.Email(invoice); 37 | } 38 | else 39 | { 40 | if (!_security.IsAdmin()) 41 | throw new UserNotAuthorizedException(); 42 | 43 | _writer.Print(invoice); 44 | 45 | invoice.LastPrintedBy = _security.GetUserName(); 46 | 47 | _database.Save(); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Module6/Hard/PrintOrEmailInvoiceCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Moq; 7 | using Moq.AutoMock; 8 | using NUnit.Framework; 9 | using TestableCodeDemos.Module6.Shared; 10 | 11 | namespace TestableCodeDemos.Module6.Hard 12 | { 13 | [TestFixture] 14 | public class PrintOrEmailInvoiceCommandTests 15 | { 16 | private PrintOrEmailInvoiceCommand _command; 17 | private AutoMocker _mocker; 18 | private Invoice _invoice; 19 | 20 | private const int InvoiceId = 1; 21 | private const string EmailAddress = "email@test.com"; 22 | private const string UserName = "mrenze"; 23 | 24 | [SetUp] 25 | public void SetUp() 26 | { 27 | _invoice = new Invoice(); 28 | 29 | _mocker = new AutoMocker(); 30 | 31 | _mocker.GetMock(). 32 | Setup(p => p.GetInvoice(InvoiceId)) 33 | .Returns(_invoice); 34 | 35 | _mocker.GetMock() 36 | .Setup(p => p.IsAdmin()) 37 | .Returns(true); 38 | 39 | _mocker.GetMock() 40 | .Setup(p => p.GetUserName()) 41 | .Returns(UserName); 42 | 43 | _command = _mocker.CreateInstance(); 44 | } 45 | 46 | [Test] 47 | public void TestExecuteForEmailingInvoiceWithNoEmailAddressShouldThrowException() 48 | { 49 | _invoice.EmailAddress = string.Empty; 50 | 51 | Assert.That(() => _command.Execute(InvoiceId, true), 52 | Throws.TypeOf()); 53 | } 54 | 55 | [Test] 56 | public void TestExecuteShouldEmailInvoiceIfEmailing() 57 | { 58 | _invoice.EmailAddress = EmailAddress; 59 | 60 | _command.Execute(InvoiceId, true); 61 | 62 | _mocker.GetMock() 63 | .Verify(p => p.Email(_invoice), 64 | Times.Once); 65 | } 66 | 67 | [Test] 68 | public void TestExecuteShouldThrowExceptionIfPrintingAndUserIsNotAdmin() 69 | { 70 | _mocker.GetMock() 71 | .Setup(p => p.IsAdmin()) 72 | .Returns(false); 73 | 74 | Assert.That(() => _command.Execute(InvoiceId, false), 75 | Throws.TypeOf()); 76 | } 77 | 78 | [Test] 79 | public void TestExecuteShouldPrintInvoiceIfPrinting() 80 | { 81 | _command.Execute(InvoiceId, false); 82 | 83 | _mocker.GetMock() 84 | .Verify(p => p.Print(_invoice), 85 | Times.Once); 86 | } 87 | 88 | [Test] 89 | public void TestExecuteShouldSetLastPrintedByToCurrentUserIfPrinting() 90 | { 91 | _command.Execute(InvoiceId, false); 92 | 93 | Assert.That(_invoice.LastPrintedBy, 94 | Is.EqualTo("mrenze")); 95 | } 96 | 97 | [Test] 98 | public void TestExecuteShouldSaveChangesToDatabaseIfPrinting() 99 | { 100 | _command.Execute(InvoiceId, false); 101 | 102 | _mocker.GetMock() 103 | .Verify(p => p.Save(), 104 | Times.Once); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Module6/Shared/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class Database : IDatabase 8 | { 9 | public Invoice GetInvoice(int invoiceId) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public void Save() 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Module6/Shared/EmailAddressIsBlankException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class EmailAddressIsBlankException : Exception 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Module6/Shared/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public interface IDatabase 8 | { 9 | Invoice GetInvoice(int invoiceId); 10 | 11 | void Save(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module6/Shared/IEmailValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TestableCodeDemos.Module6.Shared 8 | { 9 | public interface IEmailValidator 10 | { 11 | bool IsValid(string address); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module6/Shared/IInvoiceEmailer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public interface IInvoiceEmailer 8 | { 9 | void Email(Invoice invoice); 10 | } 11 | } -------------------------------------------------------------------------------- /Module6/Shared/IInvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public interface IInvoiceWriter 8 | { 9 | void Print(Invoice invoice); 10 | } 11 | } -------------------------------------------------------------------------------- /Module6/Shared/ISecurity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public interface ISecurity 8 | { 9 | string GetUserName(); 10 | 11 | bool IsAdmin(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Module6/Shared/Invoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class Invoice 8 | { 9 | public string EmailAddress { get; set; } 10 | 11 | public string LastPrintedBy { get; set; } 12 | 13 | // Other invoice fields and methods would go here 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Module6/Shared/InvoiceWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class InvoiceWriter : IInvoiceWriter 8 | { 9 | public void Print(Invoice invoice) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Module6/Shared/Security.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class Security : ISecurity 8 | { 9 | private string _userName; 10 | private bool _isAdmin; 11 | 12 | public void SetUser(string userName, bool isAdmin) 13 | { 14 | _userName = userName; 15 | 16 | _isAdmin = isAdmin; 17 | } 18 | 19 | public string GetUserName() 20 | { 21 | return _userName; 22 | } 23 | 24 | public bool IsAdmin() 25 | { 26 | return _isAdmin; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Module6/Shared/UserNotAuthorizedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TestableCodeDemos.Module6.Shared 6 | { 7 | public class UserNotAuthorizedException : Exception 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Writing Testable Code 2 | Sample code for my course on [Writing Testable Code](https://pluralsight.pxf.io/testable-code) in .NET 9.0. 3 | 4 | This sample application is intended to be an educational resource for learning testable coding practices. It incorporates these best practices in ways that are simple and easy to understand. 5 | 6 | ## Technologies 7 | This demo application uses the following technologies: 8 | - .NET 9.0 9 | - C# .NET 12.0 10 | - NUnit 4.3 11 | - Moq 4.20 12 | - Moq.AutoMock 3.5 13 | - Ninject 3.3 14 | 15 | ## Other versions 16 | For other versions of this sample application, please see the following: 17 | - [.NET 8.0](https://github.com/matthewrenze/writing-testable-code/tree/net-8) 18 | - [.NET 6.0](https://github.com/matthewrenze/writing-testable-code/tree/net-6) 19 | - [.NET 4.8](https://github.com/matthewrenze/writing-testable-code/tree/net-48) 20 | - [.NET 4.5](https://github.com/matthewrenze/writing-testable-code/tree/net-45) 21 | -------------------------------------------------------------------------------- /TestableCodeDemos.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /TestableCodeDemos.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32811.315 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableCodeDemos", "TestableCodeDemos.csproj", "{5D07C399-7099-44BC-8A13-BEBE26AF92CD}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5D07C399-7099-44BC-8A13-BEBE26AF92CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5D07C399-7099-44BC-8A13-BEBE26AF92CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5D07C399-7099-44BC-8A13-BEBE26AF92CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5D07C399-7099-44BC-8A13-BEBE26AF92CD}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {48595342-2537-492F-A131-F50C9F2336F8} 24 | EndGlobalSection 25 | EndGlobal 26 | --------------------------------------------------------------------------------