SlideShare a Scribd company logo
Breaking Dependencies
to Allow UnitTesting
Steve Smith
CTO, Falafel Software
@ardalis | ardalis.com
Tweet Away
• LiveTweeting and Photos are encouraged
• Questions and Feedback are welcome
• Use #DevIntersection and/or #breakDependencies
• Or #DevIntersectionBreakingDependenciesToAllowUnitTesting (55 chars with space)
Pluralsight
I have some 1-month free
passes; see me after if
you’d like one
Legacy Code
“To me, legacy code is simply code without tests.”
Michael Feathers
Working Effectively with Legacy Code
UnitTesting (Legacy) Code is…
Here’s (Mostly)Why…
Hollywood made a whole movie about it…
But let’s back up…
• Why UnitTests?
• Why not just use other kinds of tests?
• What are dependencies?
• How do we break these dependencies?
UnitTest Characteristics
• Test a single unit of code
• A method, or at most, a class
• Do not test Infrastructure concerns
• Database, filesystem, etc.
• Do not test “through” the UI
• Just code testing code; no screen readers, etc.
UnitTests are (should be) FAST
• No dependencies means
1000s of tests per second
• Run them constantly
UnitTests are SMALL
• Testing one thing should be simple
• If not, can it be made simpler?
• Should be quick to write
UnitTest Naming
• Descriptive And Meaningful Phrases (DAMP)
• NameTest Class: ClassNameMethodNameShould
• NameTest Method: DoSomethingGivenSomething
• http://ardalis.com/unit-test-naming-convention
Seams
• Represent areas of code where pieces can be decoupled
• Testable code has many seams; legacy code has few, if any
Kinds ofTests
UI
Integration
Tests
UnitTests
http://martinfowler.com/bliki/TestPyramid.html
Ask yourself:
•Can I test this scenario with a UnitTest?
•Can I test it with an IntegrationTest?
• Can I test it with an automated UITest?
UI
Integration Tests
UnitTests
Don’t believe your test runner…
IntegrationTest
UnitTest?
• Requires a database or file?
• Sends emails?
• Must be executed through the UI?
Not a unit test
Dependencies and Coupling
All dependencies point toward
infrastructure
Presentation
Layer
Business Layer
Infrastructure
Data Access
Tests
Tests (and everything else) now
depend on Infrastructure
Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should
depend on abstractions.
Abstractions should not depend on details. Details should depend on
abstractions.
Agile Principles, Patterns, and Practices in C#
Depend on Abstractions
All dependencies point toward
Business Logic / Core
Presentation
Layer
Business Layer
Infrastructure
Data Access
UnitTests
Integration
Tests
UITests
Inject Dependencies
• Classes should follow Explicit Dependencies Principle
• http://deviq.com/explicit-dependencies-principle
• Prefer Constructor Injection
• Classes cannot be created in an invalid state
https://flic.kr/p/5QsGnB
Common Dependencies to Decouple
•Database
•File System
•Email
•Web APIs
•System Clock
•Configuration
•Thread.Sleep
•Random
Tight Couplers: Statics and new
• Avoid static cling
• Calling static methods with side effects
• Remember: new is glue
• Avoid gluing your code to a specific implementation
• Simple types and value objects usually OK
Coupling Code Smells
• Learn more in my Refactoring Fundamentals course on Pluralsight
• http://www.pluralsight.com/courses/refactoring-fundamentals
• Coupling Smells introduce tight coupling between parts of a system
Feature Envy
• Characterized by many getter calls
• Instead, try to package data and behavior together
• Keep together things that change together
• Common Closure Principle – Classes that change together are packaged together
public class Rental
{
private Movie _movie;
public decimal GetPrice()
{
if (_movie.IsNewRelease)
{
if (_movie.IsChildrens)
{
return 4;
}
return 5;
}
if (_movie.IsChildrens)
{
return 2;
}
return 3;
}
}
public class Movie
{
public bool IsNewRelease { get; set; }
public bool IsChildrens { get; set; }
public string Title { get; set; }
public decimal GetPrice()
{
if (IsNewRelease)
{
if (IsChildrens)
{
return 4;
}
return 5;
}
if (IsChildrens)
{
return 2;
}
return 3;
}
}
Law of Demeter
• Or “Strong Suggestion of Demeter”
• A Method m on an object O should only call methods on
• O itself
• m’s parameters
• Objects created within m
• O’s direct fields and properties
• Global variables and static methods
public void GetPaidByCustomer(Customer customer)
{
decimal payment = 12.00;
var wallet = customer.Wallet;
if(wallet.Total > payment)
{
wallet.RemoveMoney(payment);
}
else
{
// come back later to get paid
}
}
public class Customer
{
private Wallet _wallet;
public decimal RequestPayment(decimal amount)
{
if(_wallet != null && _wallet.Total > amount)
{
_wallet.RemoveMoney(amount);
return amount;
}
return 0;
}
}
public void GetPaidByCustomer(Customer customer)
{
decimal payment = 12.00;
decimal amountPaid = customer.RequestPayment(payment);
if(amountPaid == payment)
{
// say thank you and provide a receipt
}
else
{
// come back later to get paid
}
}
http://geek-and-poke.com
Constructor Smells
• new keyword (or static calls) in constructor or field declaration
• Anything more than field assignment!
• Database access in constructor
• Complex object graph construction
• Conditionals or Loops
Good Constructors
• Do not create collaborators, but instead accept them as parameters
• Use a Factory for complex object graph creation
• Avoid instantiating fields at declaration
“
”
IoC Containers are just factories on
steroids.
Don’t be afraid to use them where they can help
public class MoviesController : Controller
{
private MovieDBContext _db = new MovieDBContext();
private UserManager _userManager;
public MoviesController()
{
_userManager = new SqlUserManager();
}
}
[Test]
public void TestSomeMethod()
{
var controller = new MoviesController();
// Boom! Cannot create without a database
}
public class MoviesController : Controller
{
private MovieDBContext _db;
private UserManager _userManager;
public MoviesController(MovieDbContext dbContext,
userManager)
{
_db = dbContext;
_userManager = userManager;
}
}
[Test]
public void TestSomeMethod()
{
var controller = new MoviesController(fakeContext,
fakeUserManager);
// continue test here
}
public class HomeController: Controller
{
private User _user;
private string _displayMode;
public HomeController()
{
_user = HttpContext.Current.User;
_displayMode = Config.AppSettings[“dispMode”];
}
}
[Test]
public void TestSomeMethod()
{
var controller = new HomeController();
// Boom! Cannot create an active HttpContext
// Also, how to vary display mode when there is one
// configuration file for the whole test project?
}
public class HomeController: Controller
{
private User _user;
private string _displayMode;
public HomeController(User user, IConfig config)
{
_user = user;
_displayMode = config.DisplayMode;
}
}
[Test]
public void TestSomeMethod()
{
var config = new Config() {DisplayMode=“Landscape”};
var user = new User();
var controller = new HomeController(user,config);
}
Avoid Initialize Methods
• Moving code out of the constructor and into Init()
• If called from constructor, no different
• If called later, leaves object in invalid state until called
• Object has too many responsibilities
• If Initialize depends on infrastructure, object will still be hard to test
public class SomeService
{
private IUserRepository _userRepository;
private DnsRecord _dnsRecord;
public SomeService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void Initialize()
{
string ip = Server.GetAvailableIpAddress();
_dnsRecord = DNS.Associate(“SomeService”, ip);
}
}
[Test]
public void TestSomeMethod()
{
// I can construct SomeService, but how do I test it
// when every method depends on Initialize() ?
}
public class SomeService
{
private IUserRepository _userRepository;
private DnsRecord _dnsRecord;
public SomeService(IUserRepository userRepository,
DnsRecord dnsRecord)
{
_userRepository = userRepository;
_dnsRecord = dnsRecord;
}
// initialize code moved to factory
[Test]
public void TestSomeMethod()
{
var service = new SomeService(testRepo,
testDnsRecord);
}
“Test” Constructors
• “It’s OK, I’ll provide an “extra” constructor my tests can use!”
• Great! As long as we don’t have to test any other classes that use the other
constructor.
public class SomeService
{
private IUserRepository _userRepository;
// testable constructor
public SomeService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void SomeService()
{
_userRepository = new EfUserRepository();
}
}
// how can we test this?
public void SomeMethodElsewhere()
{
var result = new SomeService().DoSomething();
}
Avoid Digging into Collaborators
• Pass in the specific object(s) you need
• Avoid using “Context” or “Manager” objects to access additional
dependencies
• Violates Law of Demeter: Context.SomeItem.Foo()
• Suspicious Names: environment, principal, container
• Symptoms:Tests have mocks that return mocks
public class TaxCalculator
{
private TaxTable _taxTable;
…
public decimal ComputeTax(User user, Invoice invoice)
{
var address = user.Address;
var amount = invoice.Subtotal;
var rate = _taxTable.GetTaxRate(address);
return amount * rate;
}
}
// tests must now create users and invoices
// instead of just passing in address and subtotal amount
public class TaxCalculator
{
private TaxTable _taxTable;
…
public decimal ComputeTax(Address address,
decimal subtotal)
{
var rate = _taxTable.GetTaxRate(address);
return subtotal * rate;
}
}
// API is now more honest about what it actually requires
// Tests are much simpler to write
Avoid Global State Access
• Singletons
• Static fields or methods
• Static initializers
• Registries
• Service Locators
Singletons
• Avoid classes that implement their own lifetime tracking
• GoF Singleton Pattern
• It’s OK to have a container manage object lifetime and enforce having only a
single instance of a class within your application
public class TrainScheduler
{
public Track FindAvailableTrack()
{
// loop through available tracks
if(TrackStatusChecker.IsAvailable(track))
// do something
return track;
}
}
// tests of FindAvailableTrack now depend on
// TrackStatusChecker, which is a slow web service
public class TrainScheduler
{
private TrainStatusCheckerWrapper _wrapper;
TrainScheduler(TrainStatusCheckerWrapper wrapper)
{
_wrapper = wrapper;
}
public Track FindAvailableTrack()
{
// loop through available tracks
if(_wrapper.IsAvailable(track))
// do something
return track;
}
}
// tests of FindAvailableTrack now can easily inject a
// wrapper to test the behavior of FindAvailableTrack()
// wrapper could just as easily be an interface
Summary
• Inject Dependencies
• Remember “New is glue”.
• Keep yourAPIs honest
• Remember the Explicit Dependencies Principle.Tell your friends.
• Maintain seams and keep coupling loose
Thanks!
• Questions?
• Follow me at @ardalis
• Check out http://DevIQ.com for more on these topics
• Take Pride inYour Code!
• References
• http://misko.hevery.com/code-reviewers-guide/
• Working Effectively with Legacy Code
by Michael Feathers

More Related Content

PDF
Breaking Dependencies to Allow Unit Testing
PPTX
Improving the Quality of Existing Software
PPTX
Improving the Quality of Existing Software
PPTX
Improving the Quality of Existing Software - DevIntersection April 2016
PPTX
Breaking Dependencies to Allow Unit Testing - DevIntersection Spring 2016
PPTX
Improving the Quality of Existing Software
PPTX
Improving The Quality of Existing Software
PDF
Add Some DDD to Your ASP.NET MVC, OK?
Breaking Dependencies to Allow Unit Testing
Improving the Quality of Existing Software
Improving the Quality of Existing Software
Improving the Quality of Existing Software - DevIntersection April 2016
Breaking Dependencies to Allow Unit Testing - DevIntersection Spring 2016
Improving the Quality of Existing Software
Improving The Quality of Existing Software
Add Some DDD to Your ASP.NET MVC, OK?

What's hot (20)

PPTX
Design Pattern Mastery - Momentum Dev Con 19 Apr 2018
PPTX
Power-Up Your Test Suite with OLE Automation by Joshua Russell
PPTX
Building unit tests correctly with visual studio 2013
PPTX
Battle of The Mocking Frameworks
PPTX
Introducing domain driven design - dogfood con 2018
PPTX
Improving the Design of Existing Software
PDF
How To Use Selenium Successfully
PDF
The Cowardly Test-o-Phobe's Guide To Testing
PDF
How to grow your own Microservice?
PPTX
Chegg - iOS @ Scale
PPTX
Working Effectively With Legacy Code
PDF
Introduction to Unit Testing, BDD and Mocking using TestBox & MockBox at Into...
PDF
Getting Ahead of Delivery Issues with Deep SDLC Analysis by Donald Belcham
PDF
Introduction to Unit Testing, BDD and Mocking using TestBox & MockBox at Adob...
PPTX
Системный взгляд на параллельный запуск Selenium тестов
PPTX
Testable requirements
PPTX
IoC and Mapper in C#
PPTX
Как стать синьором
PDF
Practical Test Automation Deep Dive
PDF
Better Page Object Handling with Loadable Component Pattern
Design Pattern Mastery - Momentum Dev Con 19 Apr 2018
Power-Up Your Test Suite with OLE Automation by Joshua Russell
Building unit tests correctly with visual studio 2013
Battle of The Mocking Frameworks
Introducing domain driven design - dogfood con 2018
Improving the Design of Existing Software
How To Use Selenium Successfully
The Cowardly Test-o-Phobe's Guide To Testing
How to grow your own Microservice?
Chegg - iOS @ Scale
Working Effectively With Legacy Code
Introduction to Unit Testing, BDD and Mocking using TestBox & MockBox at Into...
Getting Ahead of Delivery Issues with Deep SDLC Analysis by Donald Belcham
Introduction to Unit Testing, BDD and Mocking using TestBox & MockBox at Adob...
Системный взгляд на параллельный запуск Selenium тестов
Testable requirements
IoC and Mapper in C#
Как стать синьором
Practical Test Automation Deep Dive
Better Page Object Handling with Loadable Component Pattern
Ad

Viewers also liked (13)

PPT
Biodiversity I
PDF
EPSON008.PDF
PPTX
Mariela
PDF
UK Tamil nadu and Puducherry business meet
PPT
Day 1: Innovation in parliaments #1, Mr. Rovil Kumar, Head of IT, Parliament,...
PDF
Stephen Geri: What To Keep In Mind When Buying New Clubs
PPTX
.NET программирование - 1 Введение
PPTX
Aprendizaje autónomo héctor rosales
ODP
Implementing Domain Event with Akka
PPT
Workshop ferramentas web 2.0
PDF
Tourism measurement - Economical measurements
PDF
The Brand Journalist's Toolbox
PDF
Reaching Production Faster with Containers in Testing
Biodiversity I
EPSON008.PDF
Mariela
UK Tamil nadu and Puducherry business meet
Day 1: Innovation in parliaments #1, Mr. Rovil Kumar, Head of IT, Parliament,...
Stephen Geri: What To Keep In Mind When Buying New Clubs
.NET программирование - 1 Введение
Aprendizaje autónomo héctor rosales
Implementing Domain Event with Akka
Workshop ferramentas web 2.0
Tourism measurement - Economical measurements
The Brand Journalist's Toolbox
Reaching Production Faster with Containers in Testing
Ad

Similar to Breaking Dependencies to Allow Unit Testing (20)

PDF
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
PDF
How to Build Your Own Test Automation Framework?
PDF
Test Driven Development with JavaFX
PDF
Staying Sane with Drupal (A Develper's Survival Guide)
PPTX
Unit tests and TDD
PPTX
Testing the Untestable
PPT
Stopping the Rot - Putting Legacy C++ Under Test
PPTX
Qt test framework
 
PDF
Unit Testing
PDF
Testcontainers - Geekout EE 2017 presentation
PPTX
presentation des google test dans un contexte de tdd
PDF
谷歌 Scott-lessons learned in testability
KEY
Unit Testing in SharePoint 2010
PDF
Test driven development
PDF
Some testing - Everything you should know about testing to go with @pedro_g_s...
PPTX
Building unit tests correctly
PPTX
VT.NET 20160411: An Intro to Test Driven Development (TDD)
PPTX
Advance unittest
PPTX
Testing ASP.NET - Progressive.NET
PPTX
Testing Ext JS and Sencha Touch
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
How to Build Your Own Test Automation Framework?
Test Driven Development with JavaFX
Staying Sane with Drupal (A Develper's Survival Guide)
Unit tests and TDD
Testing the Untestable
Stopping the Rot - Putting Legacy C++ Under Test
Qt test framework
 
Unit Testing
Testcontainers - Geekout EE 2017 presentation
presentation des google test dans un contexte de tdd
谷歌 Scott-lessons learned in testability
Unit Testing in SharePoint 2010
Test driven development
Some testing - Everything you should know about testing to go with @pedro_g_s...
Building unit tests correctly
VT.NET 20160411: An Intro to Test Driven Development (TDD)
Advance unittest
Testing ASP.NET - Progressive.NET
Testing Ext JS and Sencha Touch

More from Steven Smith (18)

PPTX
Clean architecture with asp.net core by Ardalis
PDF
Finding Patterns in the Clouds - Cloud Design Patterns
PPTX
Introducing Domain Driven Design - codemash
PPTX
Most Useful Design Patterns
PPTX
Introducing ASP.NET Core 2.0
PPTX
Decoupling with Domain Events
PPTX
A Whirldwind Tour of ASP.NET 5
PPTX
Domain events
PPTX
My Iraq Experience
PDF
Domain-Driven Design with ASP.NET MVC
PPTX
Refactoring with SOLID Principles (FalafelCon 2013)
PPTX
Common ASP.NET Design Patterns - Telerik India DevCon 2013
PPTX
Refactoring with SOLID - Telerik India DevCon 2013
PPTX
Refactoring Applications using SOLID Principles
PPTX
Common asp.net design patterns aspconf2012
PPTX
Common design patterns (migang 16 May 2012)
PDF
Introducing Pair Programming
PPTX
Cinci ug-january2011-anti-patterns
Clean architecture with asp.net core by Ardalis
Finding Patterns in the Clouds - Cloud Design Patterns
Introducing Domain Driven Design - codemash
Most Useful Design Patterns
Introducing ASP.NET Core 2.0
Decoupling with Domain Events
A Whirldwind Tour of ASP.NET 5
Domain events
My Iraq Experience
Domain-Driven Design with ASP.NET MVC
Refactoring with SOLID Principles (FalafelCon 2013)
Common ASP.NET Design Patterns - Telerik India DevCon 2013
Refactoring with SOLID - Telerik India DevCon 2013
Refactoring Applications using SOLID Principles
Common asp.net design patterns aspconf2012
Common design patterns (migang 16 May 2012)
Introducing Pair Programming
Cinci ug-january2011-anti-patterns

Recently uploaded (20)

PDF
cuic standard and advanced reporting.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Electronic commerce courselecture one. Pdf
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Approach and Philosophy of On baking technology
PPTX
Big Data Technologies - Introduction.pptx
PDF
Empathic Computing: Creating Shared Understanding
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
cuic standard and advanced reporting.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Reach Out and Touch Someone: Haptics and Empathic Computing
NewMind AI Monthly Chronicles - July 2025
Electronic commerce courselecture one. Pdf
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Encapsulation_ Review paper, used for researhc scholars
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Per capita expenditure prediction using model stacking based on satellite ima...
Approach and Philosophy of On baking technology
Big Data Technologies - Introduction.pptx
Empathic Computing: Creating Shared Understanding
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
Dropbox Q2 2025 Financial Results & Investor Presentation

Breaking Dependencies to Allow Unit Testing

  • 1. Breaking Dependencies to Allow UnitTesting Steve Smith CTO, Falafel Software @ardalis | ardalis.com
  • 2. Tweet Away • LiveTweeting and Photos are encouraged • Questions and Feedback are welcome • Use #DevIntersection and/or #breakDependencies • Or #DevIntersectionBreakingDependenciesToAllowUnitTesting (55 chars with space)
  • 3. Pluralsight I have some 1-month free passes; see me after if you’d like one
  • 4. Legacy Code “To me, legacy code is simply code without tests.” Michael Feathers Working Effectively with Legacy Code
  • 7. Hollywood made a whole movie about it…
  • 8. But let’s back up… • Why UnitTests? • Why not just use other kinds of tests? • What are dependencies? • How do we break these dependencies?
  • 9. UnitTest Characteristics • Test a single unit of code • A method, or at most, a class • Do not test Infrastructure concerns • Database, filesystem, etc. • Do not test “through” the UI • Just code testing code; no screen readers, etc.
  • 10. UnitTests are (should be) FAST • No dependencies means 1000s of tests per second • Run them constantly
  • 11. UnitTests are SMALL • Testing one thing should be simple • If not, can it be made simpler? • Should be quick to write
  • 12. UnitTest Naming • Descriptive And Meaningful Phrases (DAMP) • NameTest Class: ClassNameMethodNameShould • NameTest Method: DoSomethingGivenSomething • http://ardalis.com/unit-test-naming-convention
  • 13. Seams • Represent areas of code where pieces can be decoupled • Testable code has many seams; legacy code has few, if any
  • 15. Ask yourself: •Can I test this scenario with a UnitTest? •Can I test it with an IntegrationTest? • Can I test it with an automated UITest? UI Integration Tests UnitTests
  • 16. Don’t believe your test runner… IntegrationTest
  • 17. UnitTest? • Requires a database or file? • Sends emails? • Must be executed through the UI? Not a unit test
  • 18. Dependencies and Coupling All dependencies point toward infrastructure Presentation Layer Business Layer Infrastructure Data Access Tests Tests (and everything else) now depend on Infrastructure
  • 19. Dependency Inversion Principle High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions. Agile Principles, Patterns, and Practices in C#
  • 20. Depend on Abstractions All dependencies point toward Business Logic / Core Presentation Layer Business Layer Infrastructure Data Access UnitTests Integration Tests UITests
  • 21. Inject Dependencies • Classes should follow Explicit Dependencies Principle • http://deviq.com/explicit-dependencies-principle • Prefer Constructor Injection • Classes cannot be created in an invalid state https://flic.kr/p/5QsGnB
  • 22. Common Dependencies to Decouple •Database •File System •Email •Web APIs •System Clock •Configuration •Thread.Sleep •Random
  • 23. Tight Couplers: Statics and new • Avoid static cling • Calling static methods with side effects • Remember: new is glue • Avoid gluing your code to a specific implementation • Simple types and value objects usually OK
  • 24. Coupling Code Smells • Learn more in my Refactoring Fundamentals course on Pluralsight • http://www.pluralsight.com/courses/refactoring-fundamentals • Coupling Smells introduce tight coupling between parts of a system
  • 25. Feature Envy • Characterized by many getter calls • Instead, try to package data and behavior together • Keep together things that change together • Common Closure Principle – Classes that change together are packaged together
  • 26. public class Rental { private Movie _movie; public decimal GetPrice() { if (_movie.IsNewRelease) { if (_movie.IsChildrens) { return 4; } return 5; } if (_movie.IsChildrens) { return 2; } return 3; } }
  • 27. public class Movie { public bool IsNewRelease { get; set; } public bool IsChildrens { get; set; } public string Title { get; set; } public decimal GetPrice() { if (IsNewRelease) { if (IsChildrens) { return 4; } return 5; } if (IsChildrens) { return 2; } return 3; } }
  • 28. Law of Demeter • Or “Strong Suggestion of Demeter” • A Method m on an object O should only call methods on • O itself • m’s parameters • Objects created within m • O’s direct fields and properties • Global variables and static methods
  • 29. public void GetPaidByCustomer(Customer customer) { decimal payment = 12.00; var wallet = customer.Wallet; if(wallet.Total > payment) { wallet.RemoveMoney(payment); } else { // come back later to get paid } }
  • 30. public class Customer { private Wallet _wallet; public decimal RequestPayment(decimal amount) { if(_wallet != null && _wallet.Total > amount) { _wallet.RemoveMoney(amount); return amount; } return 0; } }
  • 31. public void GetPaidByCustomer(Customer customer) { decimal payment = 12.00; decimal amountPaid = customer.RequestPayment(payment); if(amountPaid == payment) { // say thank you and provide a receipt } else { // come back later to get paid } }
  • 33. Constructor Smells • new keyword (or static calls) in constructor or field declaration • Anything more than field assignment! • Database access in constructor • Complex object graph construction • Conditionals or Loops
  • 34. Good Constructors • Do not create collaborators, but instead accept them as parameters • Use a Factory for complex object graph creation • Avoid instantiating fields at declaration
  • 35. “ ” IoC Containers are just factories on steroids. Don’t be afraid to use them where they can help
  • 36. public class MoviesController : Controller { private MovieDBContext _db = new MovieDBContext(); private UserManager _userManager; public MoviesController() { _userManager = new SqlUserManager(); } } [Test] public void TestSomeMethod() { var controller = new MoviesController(); // Boom! Cannot create without a database }
  • 37. public class MoviesController : Controller { private MovieDBContext _db; private UserManager _userManager; public MoviesController(MovieDbContext dbContext, userManager) { _db = dbContext; _userManager = userManager; } } [Test] public void TestSomeMethod() { var controller = new MoviesController(fakeContext, fakeUserManager); // continue test here }
  • 38. public class HomeController: Controller { private User _user; private string _displayMode; public HomeController() { _user = HttpContext.Current.User; _displayMode = Config.AppSettings[“dispMode”]; } } [Test] public void TestSomeMethod() { var controller = new HomeController(); // Boom! Cannot create an active HttpContext // Also, how to vary display mode when there is one // configuration file for the whole test project? }
  • 39. public class HomeController: Controller { private User _user; private string _displayMode; public HomeController(User user, IConfig config) { _user = user; _displayMode = config.DisplayMode; } } [Test] public void TestSomeMethod() { var config = new Config() {DisplayMode=“Landscape”}; var user = new User(); var controller = new HomeController(user,config); }
  • 40. Avoid Initialize Methods • Moving code out of the constructor and into Init() • If called from constructor, no different • If called later, leaves object in invalid state until called • Object has too many responsibilities • If Initialize depends on infrastructure, object will still be hard to test
  • 41. public class SomeService { private IUserRepository _userRepository; private DnsRecord _dnsRecord; public SomeService(IUserRepository userRepository) { _userRepository = userRepository; } public void Initialize() { string ip = Server.GetAvailableIpAddress(); _dnsRecord = DNS.Associate(“SomeService”, ip); } } [Test] public void TestSomeMethod() { // I can construct SomeService, but how do I test it // when every method depends on Initialize() ? }
  • 42. public class SomeService { private IUserRepository _userRepository; private DnsRecord _dnsRecord; public SomeService(IUserRepository userRepository, DnsRecord dnsRecord) { _userRepository = userRepository; _dnsRecord = dnsRecord; } // initialize code moved to factory [Test] public void TestSomeMethod() { var service = new SomeService(testRepo, testDnsRecord); }
  • 43. “Test” Constructors • “It’s OK, I’ll provide an “extra” constructor my tests can use!” • Great! As long as we don’t have to test any other classes that use the other constructor.
  • 44. public class SomeService { private IUserRepository _userRepository; // testable constructor public SomeService(IUserRepository userRepository) { _userRepository = userRepository; } public void SomeService() { _userRepository = new EfUserRepository(); } } // how can we test this? public void SomeMethodElsewhere() { var result = new SomeService().DoSomething(); }
  • 45. Avoid Digging into Collaborators • Pass in the specific object(s) you need • Avoid using “Context” or “Manager” objects to access additional dependencies • Violates Law of Demeter: Context.SomeItem.Foo() • Suspicious Names: environment, principal, container • Symptoms:Tests have mocks that return mocks
  • 46. public class TaxCalculator { private TaxTable _taxTable; … public decimal ComputeTax(User user, Invoice invoice) { var address = user.Address; var amount = invoice.Subtotal; var rate = _taxTable.GetTaxRate(address); return amount * rate; } } // tests must now create users and invoices // instead of just passing in address and subtotal amount
  • 47. public class TaxCalculator { private TaxTable _taxTable; … public decimal ComputeTax(Address address, decimal subtotal) { var rate = _taxTable.GetTaxRate(address); return subtotal * rate; } } // API is now more honest about what it actually requires // Tests are much simpler to write
  • 48. Avoid Global State Access • Singletons • Static fields or methods • Static initializers • Registries • Service Locators
  • 49. Singletons • Avoid classes that implement their own lifetime tracking • GoF Singleton Pattern • It’s OK to have a container manage object lifetime and enforce having only a single instance of a class within your application
  • 50. public class TrainScheduler { public Track FindAvailableTrack() { // loop through available tracks if(TrackStatusChecker.IsAvailable(track)) // do something return track; } } // tests of FindAvailableTrack now depend on // TrackStatusChecker, which is a slow web service
  • 51. public class TrainScheduler { private TrainStatusCheckerWrapper _wrapper; TrainScheduler(TrainStatusCheckerWrapper wrapper) { _wrapper = wrapper; } public Track FindAvailableTrack() { // loop through available tracks if(_wrapper.IsAvailable(track)) // do something return track; } } // tests of FindAvailableTrack now can easily inject a // wrapper to test the behavior of FindAvailableTrack() // wrapper could just as easily be an interface
  • 52. Summary • Inject Dependencies • Remember “New is glue”. • Keep yourAPIs honest • Remember the Explicit Dependencies Principle.Tell your friends. • Maintain seams and keep coupling loose
  • 53. Thanks! • Questions? • Follow me at @ardalis • Check out http://DevIQ.com for more on these topics • Take Pride inYour Code! • References • http://misko.hevery.com/code-reviewers-guide/ • Working Effectively with Legacy Code by Michael Feathers

Editor's Notes

  • #3: https://twitter.com/hashtag/devintersection https://twitter.com/hashtag/breakdependencies
  • #5: And specifically, we want fast, automated tests.
  • #7: Developers like to build things, but often the way they connect parts of an application together (if there even are separate parts) is like using super glue. Good luck testing the interface of one building block.
  • #8: If you haven’t seen it yet, you should. This guy wants to use krazy glue to keep anything from changing. Probably not how we want our software to be designed.
  • #9: I want to answer these questions in this session. 1. 2. 3.
  • #12: …unless the system is difficult to break apart
  • #13: Avoid names that provide no information: CustomerTests Test1 Test2 Test3
  • #14: NEED GRAPHIC HERE FOR SEAM – maybe pulling apart two legos, or photo of a carpentry joint
  • #15: Because they’re small and fast, you should have a lot of unit tests. They should be the foundation of your testing approach. Integration tests can include tests of services that sit between different layers of your application. Defense in Depth
  • #16: Test at the lowest level you can, since that will be the least expensive test to write, run, and maintain.
  • #17: ReSharper calls any test you run a unit test, regardless of what kind of test it actually is.
  • #18: Also, they shouldn’t be terribly complex, slow, or involve more than one or two classes.
  • #19: Dependencies are transitive. If A depends on B and B depends on C, then A depends on C.
  • #27: This can be difficult to test, especially if either of the two classes in question are difficult to set up in a particular state. To fix this, move the behavior where it belongs.
  • #28: This can of course be refactored further to eliminate the conditional complexity, using inheritance or a compositional pattern.
  • #29: “Well, the code is more like guidelines than rules.” Assume as little as possible about collaborators’ collaborators
  • #30: Imagine when you order a pizza, the delivery person uses code like this to get paid. Does this seem to model how things actually work? Should the delivery person have access to your wallet and its contents? What if you don’t have a wallet? This code is now tightly coupled not only to a Customer, but to a Wallet and its implementation.
  • #31: What if instead the Customer could respond to requests for payment, like this? Note that wallet is no longer exposed outside of the Customer class.
  • #32: At this point, the original code makes a bit more sense. Payment is requested, and how it is achieved is no longer a concern of the calling code. Payment can come from a money clip, purse, or from under a mattress and this code will still work.
  • #33: Drink water
  • #34: Constructors should be extremely simple. They shouldn’t choose the object’s collaborators, or how the object is retrieved from persistence. These things violate the Single Responsibility Principle for the constructor’s class, and tightly couple the class to certain implementation details. Constructors that accept collaborators provide seams – hard-coding collaborators erases such seams. New is probably OK for initializing simple things like strings, datetimes, or collections to avoid nulls. Prefer the new C# 6 syntax for this with properties.
  • #35: Note, it’s fine to create simple types and value objects – simply consider whether it’s making the object harder to test or not when deciding.
  • #37: This code is difficult to test and tightly coupled to a dbContext, which in this case probably makes it impossible to test this code without a database.
  • #38: Note that in this case the only thing the constructor does is assign parameters to fields, and no fields are instantiated as part of their declaration.
  • #39: Likewise, avoid making static lookup calls or assignments within a constructor, especially ones that depend on infrastructure or the local environment. Avoid depending on configuration directly – remember it’s a dependency.
  • #40: The solution is the same – avoid creating things in the constructor in favor of passing these things in. If you can’t pass in the thing itself, pass in a means of getting to it (like we’re doing with IConfig in this case), but keep it simple as possible.
  • #43: Dependency injection again saves the day. The initialization logic is a separate responsibility from whatever the service does, and so belongs in another class.
  • #49: Adds coupling in an invisible, hard to track manner. Makes testing difficult, especially parallel testing or testing in any order Causes Spooky Action at a Distance – your method causes some unexpected behavior in some other part of the system
  • #51: Third party library with static call
  • #52: Use a wrapper and inject