35. Tests aren’t Tests “Test” doesn’t describe what is going on Tests are specification Tests are design Test suites are an artifact of the design process
36. TDD: Test Driven Design Write the code you wish you had Hard to test == Hard to use Better to find this out now than 2 months from now when you have to change it Done right, helps you adhere to the Principles of Object Oriented Design
37. SOLID Like a Rock http://www.flickr.com/photos/gustty/2908229462/
38. Dependency Management Dependency Management is Hard The single greatest influencer of software quality The foundation for creating software that exhibits the qualities we desire
41. Qualities of Software Undesirable Hard to change Fragile Not reusable Desirable Easy to change Robust Reusable
42. Dependency Management and SOLID SOLID is The foundational set of principles for managing dependencies Therefor the foundation for creating software that exhibits the qualities we desire
44. What is SOLID? Single Responsibility Principle Open Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle
48. Open Closed Principle You should be able to extend the behavior of a class, without modifying it Classes should be open for extension but closed for modification http://www.objectmentor.com/resources/articles/ocp.pdf
54. Dependency Inversion Principle Depend on abstractions, not on specific implementations http://www.objectmentor.com/resources/articles/dip.pdf
55. Test Driven Design The Path to Success: 3 Simple Rules http://www.flickr.com/photos/stuckincustoms/4070581709/
56. Rule #1 You are not allowed to write any production code unless it is to make a failing unit test pass.
57. Rule #2 You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
58. Rule #3 You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
59. The Flow of Test Driven Design Write a Test Watch Test Fail Write Code Watch Test Pass Refactor See step 1 http://gamesfromwithin.com/when-is-it-ok-not-to-tdd
60. TDD Demo! Fibonacci Number Generator Sequence of numbers First two numbers are 0 and 1 Each subsequent number is the sum of the two previous numbers 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
61. How To Test http://www.flickr.com/photos/modestchanges/3214701196/
64. Testing Pattern Arrange, Act, Assert Separates what is being tested from the setup and verification steps Makes some Test Smells more obvious Assertions mixed in with “Act” code. Test methods that try to test too much at once
71. Test Doubles Think “stunt double” Replacement for a Production object for testing purposes There are many types of Test Doubles, each with its own purpose
73. Fake Objects Have working implementations Usually just enough for them to work in the context of the specific test However, not suitable for Production E.g. In-Memory Database
74. Stubs Provide canned answers to SUT Usually don’t respond to anything more than what was programmed for the context of the test
75. Spies Stubs that record information on how they were called Allows inspection for Assertion Example Email Service that records the messages it was sent (without actually sending them)
76. Mocks Pre-programmed with expectations Specify how they should be called by the SUT Throws an exception if they receive a call they were not expecting Checked during the Assert to ensure they got all the calls they were expecting
77. Refactoring You could do that better… http://www.flickr.com/photos/29108968@N06/2852038148/
78. Why Refactoring? Improve human readability of code Reduce the complexity in order to improve maintainability Create a more expressive internal structure It’s about DESIGN!!
79. What is Refactoring? Adisciplinedtechnique for restructuring an existing body of code Altering code’s internal structure without changing its external behavior A series of small steps that do very little on their own Risk of change is mitigated by running automated tests after each step
80. Refactoring is NOT An undisciplined technique Making changes to code that change its external behavior One giant step Done without a safety net of automated tests
81. You Are Not Refactoring If you are doing any of those things, your are not refactoring. You’re just changing things willy-nilly. http://www.flickr.com/photos/brymo/2807243701/
82. How NOT to Test Yeah, don’t do that http://www.flickr.com/photos/bobcatnorth/1199505016/
83. Test Smells Things that make you go “that just ain’t right” Code Smells Test Smells Project Smells Most of what follows is summarized from xunitpatterns.com/Test%20Smells.html the book XUnit Test Patterns: Refactoring Test Code
84. Test Smells – Code Smells Smells that are found while looking at test code Obscure Test Conditional Test Logic Hard-to-Test Code Test Code Duplication Test Logic in Production
85. Obscure Test There is trouble understanding what behavior a test is verifying too much or too little information in the test method Possible Solutions Keep tests small and focused Do not depend on external resources (files, databases, web services, etc.)
86. Conditional Test Logic Hard to know exactly what a test is going to do when it really matters conditional logic to handle when SUT fails to return valid data Loops to verify contents of collections Conditionals to verify complex objects Possible Solutions Custom Assertion Methods
87. Hard to Test Code Code is difficult to test Highly Coupled Code Asynchronous Code Untestable Test Code Possible Solutions Reduce coupling by using TDD and adhering to SOLID principles Use Test Doubles to isolate the SUT
88. Test Code Duplication The same code is repeated many times Cut-and-Paste Code Reuse Reinventing the Wheel Possible Solutions Refactor your test code to create Utility Methods
89. Test Logic in Production Production code contains logic that should only be exercised during tests Conditional logic “For Tests Only” Test Dependency in Production Test-Specific Equality Possible Solutions Test-specific sub-classes of SUT Manage Test Dependencies Custom Assertions
90. Test Smells – Behavior Smells Smells that jump out at you while running tests Assertion Roulette Erratic Test Fragile Test Frequent Debugging Manual Intervention Slow Tests
91. Assertion Roulette Hard to tell which of several assertions in the same test caused the test to fail Test tries to verify too much Assertions with unclear failure messages Possible Solutions One logical assertion per test Include a failure message for the assertion Use a GUI test runner
92. Erratic Test One or more test pass or fail intermittently Tests depend on each other Resource Leakage / Scarcity Shared state Using different/random values for each test run Possible Solutions Use a fresh fixture for each test Clean up after yourself Use Database sandboxes or a Fake Database
93. Fragile Test A test fails to compile or run when the SUT is changed in ways that do not affect the part the test is exercising Data Sensitivity Over-specifying behavior Context Sensitivity Possible Solutions Do not depend on a database Encapsulate setup behind Creation Methods Abstract away dependencies (including Time)
94. Frequent Debugging Manual debugging is required to determine the cause of most test failures Caused by Lack of Defect Localization (missing tests) Infrequent Test Runs Possible Solutions Only create behavior after a failing test case Runs tests as part of Continuous Integration builds
95. Manual Intervention A test requires a person to do some manual action each time it is run, or verify results manually Lack of attention to automated test fixture setup Test is not self-checking Test conditions that are hard to generate programmatically Possible Solutions Invest in automating test setup Use Assertion Methods Use Test stubs to simulate test conditions
96. Slow Tests Tests take a long time to run Interaction with external resources (Databases, File Systems, Web Services) Heavy Test fixture rebuilds for every test Asynchronous Test / Explicit delays in the test Too many tests Possible Solutions Abstract away database, file system, web services Use an immutable shared fixture Separate event from the behavior it executes Run a fast subset more, and the entire suite less
97. Test Smells – Project Smells Smells that a project manager can watch out for Buggy Tests Developers Not Writing Tests High Test Maintenance Cost Production Bugs
98. Buggy Tests Bugs are regularly found in automated tests Fragile Tests Obscure Tests Hard to Test Code Possible Solutions Learn to write tests properly Refactor legacy code to make testing easier & more robust Use TDD
99. Developers Not Writing Tests The Usual Excuses Not Enough Time Hard to Test Code Wrong Test Automation Strategy Possible Solutions Learn & Practice writing tests takes less time as you become more familiar with doing it
100. High Test Maintenance Cost Too much time is spent maintaining tests Fragile Tests Obscure Tests Hard to Test code Possible Solutions Learning good Test Automation Practicing TDD
101. Production Bugs Too many bugs found during formal test or in production Infrequently Run Tests Untested Code Missing Unit Tests Lost Tests Possible Solutions Speed up test suite Use TDD Write tests to cover untested code Ensure all tests are part of some suite that is run regularly
103. Testing Frameworks NUnit, MBUnit, XUnit.net Testing frameworks in the xUnit tradition Assertion Engine with syntactical sugar on top
104. Support Libraries Object Factories NBuilder Test Doubles / Isolation Frameworks Hand-rolled mock objects Rhino Mocks Moq NMock EasyMock.NET Typemock Isolator Telerik Just Mock
105. Test Runners NUnitcommand line NUnit GUI TestDriven.NET & Visual Nunit Addins for Visual Studio JetBrainsReSharper or DevExpressCodeRush Integrates with Visual Studio test status next to the test method in the editor JetBrainsTeamCity Continuous Integration server
106. Test Guidance Pair Programming (Dev and QA) Code Reviews Automated Tools Typemock Test Lint
114. MSDN Magazine Articles June 2008 – Patterns in Practice: The Open Closed Principlehttp://msdn.microsoft.com/en-us/magazine/cc546578.aspx October 2008 – Patterns in Practice: Cohesion and Couplinghttp://msdn.microsoft.com/en-us/magazine/cc947917.aspx December 2008 – Patterns in Practice: Design for Testabilityhttp://msdn.microsoft.com/en-us/magazine/dd263069.aspx September 2007 – Unit Testing: Exploring the Continuum of Test Doubleshttp://msdn.microsoft.com/en-us/magazine/cc163358.aspx
115. Videos TDD with Kent Beckhttp://www.pragprog.com/screencasts/v-kbtdd/test-driven-development Test First and Test Driven conversations with Corey Haines and JB Rainsbergerhttp://programmingtour.blogspot.com/2009/07/test-first-and-test-driven-conversation.html Moving Specificity Towards the Tests with Corey Haines and JB Rainsbergerhttp://programmingtour.blogspot.com/2009/08/moving-specificity-towards-tests-with.html Codemanship’s Videos with Jason Gormanhttp://www.youtube.com/user/parlezuml TekPub Concepts serieshttp://tekpub.com/production/concepts
116. Podcasts Hanselminutes Podcast 145 - SOLID Principles with Uncle Bob - Robert C. Martinhttp://www.hanselminutes.com/default.aspx?showID=163 Hanselminutes Podcast 150 – Uncle Bob Martin: SOLID: This Time With Feelinghttp://www.hanselminutes.com/default.aspx?showID=168 Hanselminutes Podcast 146 – Test Driven Development is Design – The Last Word on TDD with Scott Bellwarehttp://www.hanselminutes.com/default.aspx?showID=164 Hanselminutes Podcast 31 – Test Drivenhttp://www.hanselminutes.com/default.aspx?showID=42 Coding QA Podcast 41 – iPhone TDDhttp://codingqa.com/index.php?post_id=622464 Software Engineering Radio Podcast 155 - Johannes Link & LasseKoskelaon TDDhttp://www.se-radio.net/2010/02/episode-155-johannes-link-lasse-koskela-on-tdd/
117. Web Sites The Principles of Object Oriented Designhttp://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod xUnitTest Patternshttp://xunitpatterns.com/ Object Mentor Bloghttp://blog.objectmentor.com/ Martin Fowler’s Bloghttp://martinfowler.com/bliki/ Roy Osherove’s Bloghttp://weblogs.asp.net/ROsherove/
Editor's Notes
…or that there is an impasse with the tools and you have made a conscious decision on the design
As an example, consider a module that compiles and prints a report. Such a module can be changed for two reasons. First, the content of the report can change. Second, the format of the report can change. These two things change for very different causes; one substantive, and one cosmetic. The single responsibility principle says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes or modules. It would be a bad design to couple two things that change for different reasons at different times.The reason it is important to keep a class focused on a single concern is that it makes the class more robust. Continuing with the foregoing example, if there is a change to the report compilation process, there is greater danger that the printing code will break if it is part of the same class.
When a single change results in a cascade of changes to dependent modules, you have a violation of the Open Closed Principle
A typical example that violates LSP is a Square class that derives from a Rectangle class, assuming getter and setter methods exist for both width and height. The Square class always assumes that the width is equal with the height. If a Square object is used in a context where a Rectangle is expected, unexpected behavior may occur because the dimensions of a Square cannot (or rather should not) be modified independently. Mutability is a key issue here. If Square and Rectangle had only getter methods (i.e. they were immutable objects), then no violation of LSP could occur.
In a nutshell, no client should be forced to depend on methods it does not use.