Design for testability as a way to good codingSimone ChiarettaArchitect, Council of the EUhttp://codeclimber.net.nzTwitter: @simonechDecember 9th, 2010
Who the hell am I?Simone ChiarettaMicrosoft MVP ASP.NETASP InsiderBlogger – http://codeclimber.net.nzItalianALT.NET UG FounderOpenSource developerClimberAll Around Nice GuyDisclaimer:"The viewsexpressed are purelythose of the speaker and may not in anycircumstancesberegarded as stating an official position of the Council"
What are we going to talk about?What is “Good Design”?Testability requirements?What is Design for Testability?What is Dependency Injection?What is Inversion of Control?How to do IoC via DI using Ninject?How to do IoC via DI using Unity?References
What is Good Design?
What is Good DesignHigh CohesionLow CouplingGood Encapsulation
What is Good Design
Solid: Single Responsibility Principle (SRP)A class should have one, and only one, reason to change.
Solid: SingleResponsibilityPrinciple (SRP)“If a class has more then one responsibility, then the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class’ ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed.”Robert C. MartinSolid: SingleResponsibilityPrinciple (SRP)Email Sending AppEmail SenderEmail Sending AppFlat FileXML FileFile
sOlid: Open Closed Principle (OCP)You should be able to extend a classes behavior, without modifying it.
sOlid: OpenClosedPrinciple (OCP)“Modules that conform to the open-closed principle have two primary attributes.They are “Open For Extension”. This means that the behavior of the module can be extended. That we can make the module behave in new and different ways as the requirements of the application change, or to meet the needs of new applications.They are “Closed for Modification”.The source code of such a module is inviolate. No one is allowed to make source code changes to it.”- Robert C. Martin
sOlid: OpenClosedPrinciple (OCP)Email SenderFileReaderServiceIFileFormat ReaderFlat FileXML File
soLid: Liskov Substitution Principle (LSP)Derived classes must be substitutable for their base classes.
soLid: LiskovSubstitutionPrinciple (LSP)“If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.”	- Barbara Liskov
soLid: LiskovSubstitutionPrinciple (LSP)Email SenderFileReaderServiceDatabaseIFileFormat ReaderFlat FileXML FileDatabase Connection File
soLid: LiskovSubstitutionPrinciple (LSP)DatabaseDatabaseReaderServiceEmail SenderFlat FileIFileFormat ReaderFileReaderServiceXML File
solId: Interface Segregation Principle (ISP)Clientsshouldnotbeforcedtodependuponinterfacestheydon’t use
solId: InterfaceSegregationPrinciple (ISP)“This principle deals with the disadvantages of ‘fat’ interfaces. Classes that have ‘fat’ interfaces are classes whose interfaces are not cohesive. In other words, the interfaces of the class can be broken up into groups of member functions. Each group serves a different set of clients. Thus some clients use one group of member functions, and other clients use the other groups.”	- Robert Martin
solId: InterfaceSegregationPrinciple (ISP)EmailSenderEmailSenderDatabase ReaderServiceFileReaderServiceSendEmail
ReadFile
ReadFromDb
SendEmail
GetMessageBody
GetMessageBodysoliD: Dependency Inversion Principle (DIP)Depend on abstractions, not on concretions.
soliD: DependencyInversionPrinciple (DIP)“What is it that makes a design rigid, fragile and immobile? It is the interdependence of the modules within that design. A design is rigid if it cannot be easily changed. Such rigidity is due to the fact that a single change to heavily interdependent software begins a cascade of changes in dependent modules.”	- Robert Martin
soliD: DependencyInversionPrinciple (DIP)Flat File ReaderXml File ReaderEmail SenderDatabase Reader ServiceIMessageInfoRetrieverIEmailServiceProcessingServiceIFileFormat ReaderFile Reader Service
Before and AfterIMessageInfoRetrieverIEmailSenderEmailProcessingServiceEmail Sending AppDatabaseDatabaseReaderServiceFlat FileIMessageInfoRetrieverXML FileIFileFormat ReaderFileReaderServiceFileIEmailServiceEmailServiceBeforeAfter
How to test for “good design”?You can’tActually you can Clear?
Testability Requirements
Testability ActorsSystem Under TestDepended On ComponentMock/Fake/Stub
Testability ConceptsTest just one featureIndipendency from environmentIndipendency from dependenciesFast
Design for Testability
Design for Testability = Good DesignGood design is difficult to measureEasily testable = Good Design
What is Dependency Injection
Bad CodeDemo:Hard-Coded Dependencies1-2
The problem of strong couplingRigid – Must change the Climber code to change the Tools he usesFragile – Changes to the Tools can affect the ClimbersNot Testable – Cannot replace the Tools with a stub/fake when I want to test the Climber in isolation
Better CodeDemo:Hard-Coded Dependencieswith Interface3
Still problemsWe have lower coupling but still Climber has to be changed to change tools
Slightly Better CodeDemo:Hard-Coded Dependencieswith Service Locator4
Still problemsStill Climber depends on the LocatorJust moving the problem inside another module
Introducing Dependency InjectionDemo:Dependency Injectionby Hand5
Good, isn’t it?Climber is always the same, and doesn’t know how to “get” the toolsThe Climber is given the tools he has to use
Dependency InjectionAre we done?NOT YET!
Introducing Dependency InjectionDemo:Dependency Injectionby Hand(more complex)6
Needs ImprovementsHost must know how to assemble dependenciesWeloose encapsulation
What is Inversion of Control
Inversion of ControlDemo:Inversion of Control7
What we achievedStill have DIPGot encapsulation backDependencies are handled by external component
How to configureXMLAttributesFluent APIall of them
Many IoCC…
Ninject
The KernelFactory Method on SteroidsHold the configurationReturns objectsIKernel kernel = new StandardKernel(					new ClimbingModule());var climber = kernel.Get<Climber>();
ModulesModules hold specific configurationsConfiguration through Fluent APIBind<Climber>().ToSelf();Bind<IClimbingTools>().To<QuickDraws>();
Inversion of ControlDemo:Inversion of Control(complex)8
Different kinds of InjectionConstructor InjectionProperty InjectionMethod InjectionThrough Factory Method
AttributesAre used to help discriminate injection patterns[Inject]public IClimbingTools tools {get; set;}[Inject]public void GetReady(IClimbingTools tools)
Inversion of ControlDemo:AttributesInjection Patterns9
BehavioursSingleton (Default)TransientPer ThreadPer RequestBYOBind<Climber>().ToSelf().InTransientScope();Bind<Climber>().ToSelf().InSingletonScope();
Inversion of ControlDemo:Activation Behaviours10
But there is more...Constructor ParametersContextual BindingNamed BindingBind<IClimbingTools>().To<IceScrews>() 	.WithConstructorArgument("brand",					"Black Diamond");Bind<IClimbingTools>().To<QuickDraws>()	.WhenInjectedInto(typeof(SportClimber));Bind<Climber>().To<SportClimber>()					.Named("Simone");climber = kernel.Get<Climber>("Simone");
Inversion of ControlDemo:Advanced Features11
Finally Some TestingNo need to use IoC any more (and you should not)MockTools tools = new MockTools();Climber climber = new Climber(tools);climber.Climb();Assert.IsTrue(tools.Placed);
Finally some TestingDemo:Testing12
P&P Unity
UnityMicrosoft IoC containerConfigured via codeConfigured through XMLmyContainer    .RegisterType<IClimbingTools, QuickDraws>();<unity><typeAliases>  <typeAlias alias="IClimbingTools” type="ClimbDemoIoCUnity.IClimbingTools, ClimbDemoIoCUnity" />  <typeAlias alias="QuickDraws” type="ClimbDemoIoCUnity.Tools.QuickDraws, ClimbDemoIoCUnity" /></typeAliases><containers>  <container>	<types>	  <type type="IClimbingTools" mapTo="IceScrews" />	</types>  </container></containers></unity>
UnityDemo:Unity13
Bonus section: Func
FuncBy Daniel Cazzulino (of Moq fame)The fastestIoCavailableDoesn’t usereflectionAlwayswrite factory methodcontainer.Register<IClimbingTools>(				c => newQuickDraws());container.Register(		c => newClimber(c.Resolve<IClimbingTools>()));
Bonus section: FuncDemo:Func14
IoC inside other hosts
IoC in other hostsIoC shines when activation is already delegated to factoriesASP.NET MVCWCFRequires changes in the default “object factory”ControllerFactoryServiceHostFactory
IoC in other hostsDemo:ASP.NET MVC15
Conclusions
Call for ActionsThink about a project you worked onThink about any maintainabily/change issue you had:Most likely they would have been solved with DI/IoCThink how DI/IoC could have helped
Main takeawaysDI/IoC helps building service oriented applicationsDI/IoC helps managing dependenciesDI/IoC helps bulding highly cohese, loose coupled code while maintaling encapsulation
ReferencesUncle Bob’s Principle Of Object Oriented Development: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOodOO Design Principles:http://www.oodesign.com/design-principles.htmlComplete SOLID slides and demo (Derick Bailey):http://www.lostechies.com/media/p/5415.aspxNinject:http://ninject.org/  - v2http://github.com/enkari/ninject/  - v2.2 betap&p Unity:http://unity.codeplex.com/ - v2 (part of EntLib5)Funq:http://funq.codeplex.com/

Design for testability as a way to good coding (SOLID and IoC)

Editor's Notes

  • #6 Cohesion:“A measure of how strongly-related and focused the various responsibilities of a software module are” - WikipediaCoupling:“The degree to which each program module relies on each one of the other modules” – WikipediaEncapsulation:“The hiding of design decisions in a computer program that are most likely to change” - Wikipedia
  • #7 Jenga game = tower where if you remove one piece, all the tower can collapse
  • #10 Examplemightbeanemailsendingapplicationthatsendsemailtaking the textfroman external file:Ifyouread the text and send the emailfrom the same class you are breaking the SRPprinciple.Itwouldbebetterto separate thereadingpartfrom the sending one.
  • #14 It’sallaboutbehaviours and pre/post conditions:A derivedtype can onlyweakenpre-conditionsA derivedtype can onlystreghtenpost-conditions
  • #15 Imagineyouwanttoputtemplates in a database: youcannot just write a “file” readerthatuses a databaseasitwillneed a connectionstring, and the usershould do differentthingsbased on thetype of thereader, thusmaking the substitutionnottransparent.
  • #16 Hereitwouldhavebeenbettertocompletely separate thetwothings, andhave a databasereader and a file reader.
  • #18 After adding the Databasereader, wenowhave the emailsendingservicethat can bothread file anddatabases, andobviouslysendingemails.So it’s betterto split theseresponsibilities in differentinterfaces.
  • #22 Finally,instead of having the singleobject create theirowndependencies, itis the top-mostclientthatconfigures the systemforwhatitneeds.