Title: Designing For Testability Testable designs in the real world
1Designing For TestabilityTestable designs in the
real world
- Roy Osherove
- Principal, Team Agile
- Blog ISerializable.com
2Why Should I Care?
- Maintainable System
- Extensible System
- Code Quality
3Automated Unit/FIT Testing Is Important
- Leads to better design
- Extensible System
- Helps with Regression Testing
- Maintainable System
- Finds bugs early
- Code Quality
- Better understanding of the requirements
- API Documentation
- Executable Requirements
4Testing in Context
- Engineering methodologies (waterfall for
example..) - Agile methodologies
- XP
- Automated Unit Testing
- Test Driven Development
- Customer Testing (FIT)
- The Crystal family
- MSF-Agile
- Highsmith's ASD (Adaptive Software Development)
- Scrum
- Feature Driven Development
- DSDM (Dynamic Systems Development Method)
- Rational Unified Process? (dX)
5So whats a unit test?
6A Unit Test is a testof a small functional piece
of code
Public bool IsLoginOK(string user, string
password) //
7Have you done unit testing?
8Whats an automated unit test?
- Runs without intervention
- Anyone can run it
- Can run as part of a daily build process
9The xUnit Frameworks
- Original was for SmallTalk
- Kent Beck and Erich Gamma
- Ported to Various languages and platforms
- JUnit, CppUnit, DUnit, VBUnit, RUnit, PyUnit,
Sunit, HtmlUnit, - Good list at www.xprogramming.com
- Standard test architecture
- Introducing NUnit
10Implementing a test caseNUnit
Test Public void Add_2Numbers_ReturnsSum() Cal
culator c new Calculator() int result
c.Add(1,2) Assert.AreEqual(3,result,Sum of 1
and 2 was wrong)
11The FIT Framework
- Customer Testing
- Uses HTML Tables
- Allows testing flows
- Requires Adapter classes to execute the system
under test
12Unit Level Testing Requires Testability
- Testable Code Design
- Allows Unit Testing
- FIT Testing
- Test Driven Development
- Creates Testable code
- Design Up front
- ?
13What is a Unit-Testable System
- For each piece of coded logic in the system, a
unit test can be written easily enough to verify
it works as expected while keeping the PC-COF
rules - Partial runs are possible
- Configuration is not needed
- Consistent pass/fail result
- Order does not matter
- Fast
14Consistent pass/fail result for each test
- bool CanPurchase(Person p)
-
- If(Validator.IsNotValid(p)
-
- throw new Exception()
-
- return
- p. SSID !null
- p.SubscriptionType!null
- p.CreditOnFilegt0
- Logic
- Throw if Bad validation
- Return true/false based on
- Problems
- Validator has a bug
- How do you control Validator to validate or not?
15Testable designs use Interfaces
- IValidator validator
- bool CanPurchase(Person p)
-
- If(this.validator.IsNotValid(p)
-
- throw new Exception()
-
- return
- p. SSID !null
- p.SubscriptionType!null
- p.CreditOnFilegt0
- Possible solution
- Extract Interface for Validator
- Send in a fake validator
16Demo
- Interface based design testing
17Fast run times
- bool CanPurchase(Person p)
-
- If(Validator.IsNotValid(p)
-
- throw new Exception()
-
- return
- p. SSID !null
- p.SubscriptionType!null
- p.CreditOnFilegt0
- Logic
- Throw if Bad validation
- Return true/false based on
- Problems
- Validator takes a long time
18No Configuration needed
- String Foo(Person p)
-
- String connectionString
- Settings.ConnectionString
-
19Solving Configuration with override
- String Foo(Person p)
-
- String connectionString
- getConnectionString()
-
-
- Protected virtual string getConnectionString()
-
-
- Extract dependency into virtual method
- Override method in derived class
- Used derived class in tests
20Demo
21Test Order does not matterfor consistent results
- bool Insert(Person p)
-
- //insert person to database
-
- bool Delete(Person p)
-
- //Delete person from database
- Verify insert works
- Verify Delete
- Order
- Insert has to come before delete test?
- Delete Before Insert test?
- Rollback database state between each test to make
order irrelevant. - Is that easy?
- Is it a unit test?
22Partial runs of tests produce consistent results
- bool Insert(Person p)
-
- //insert person to database
-
- bool Delete(Person p)
-
- //Delete person from database
- Verify only Delete Works
- Order
- Will is work just the same as running all tests?
- Rollback database state between each test to make
order irrelevant. - Is that easy?
- Is it a unit test?
23Are Singletons Evil?
- Single Responsibility
- Global Variables
- Tight Coupling
- State that lasts forever
- Threading?
24Refactoring Singletons
25The GOD Method
- One huge do-it-all method
- Lots of little private ones perhaps
- The design smell
- Should you test private methods?
- TDD evolves into private tested methods
26Pitfalls and traps
- Singletons statics
- Hard coded dependencies
- Configuration based objects
- Constructors that use external dependencies
- Private Stateless Methods
- Convert static to instance
- Extract Interface
- Use Fake
- Inherit override
- Use TestableXXX
- Extract hard coded dependency to different class
- Extract to helper class or static helper method
27Demos
- Hard coded dependencies
- statics
- Private Helper Methods
- Constructors that use external dependencies
28Test Driven Dev/Design
- Tests have to use a testable class
- Test-first means testable-design-first
- Decoupled design and architecture
- More control on state simulations
29BDUF
- Design just enough to have something to build
- Note that it will likely change in the future
- Design as you go and change the master plan if
needed
30Decoupled Design
- Maintainable
- Extensible
- Testable
- Replaceable parts
- More Agile Dealing with design changes becomes
easier
31Service Oriented Design
- Decoupled Services
- Independently Testable
- External Dependencies easily replaceable
32Approaching legacy code design
- Step 1 write integration tests
- Step 2- Refactor into testability
- Step 3 Write unit tests
- Design evolves
- Book Working effectively with legacy code
- Michael Feathers
33Design Guidelines
- Interface based Designs
- Try to avoid Singletons
- Avoid GOD Methods Use Polymorphism instead
- Virtual by default
- Use Factory methods or classes instead of hard
coded initializations - Single Responsibility for class and method
34Key points
- Design for testability leads to Agility
- Common patterns
- Recognizing test challenges in the design
- Refactoring the design for testability
- Test Driven Development leads to a testable
design - Testable code Well designed, decoupled design
35Thank you!
- Roy_at_TeamAgile.com
- TeamAgile.com
- ISerializable.com (blog)