Imagine a social networking website for travellers
- You need to be logged in to see the content
- You need to be a friend to see someone else's trips
- You cannot change production code if not covered by tests
- Just automated ("safe") refactorings (via IDE) are allowed, if needed to write the test
Start testing from shortest do deepest branch
- because getting to deepest requires big setup i.e. sample data, mocks, fakes etc.
- allows to understand better what the code does
git clone https://github.com/orient-man/LegacyApp.git
cd LegacyApp
git checkout 0f05084
- Visual Studio 2010/2012 (better Prof. but Express - free edition - is good enough)
- should compile without VS (with .NET 4.5 OOTB, for 4.0 try
git cherry-pick f5db872
)
- one of many NUnit test runners (NUnit standalone, NCrunch, TestDriven.NET, Mighty-Moose, VS2012 built-in + NUnit adapter, R#...)
- tip: split screen (both test and tested code)
- installing packages:
Install-Package nunit -ProjectName LegacyApp.Web.Tests
- use snippets for test class / method
- great tools: NCrunch / Resharper
- Before: 0f05084
- ShouldThrowAnExceptionWhenNotLoggedIn
- Ctrl-Shift-R / Ctrl-R Ctrl-M - Extract Method "GetLoggedInUser"
- introducing a seam into the code (to avoid HttpContext)
- code coverage shows if test covers the branch I wanted
- refactoring test
- After: b20759c
- Before: b20759c
- After: 1613b68
- Before: 1613b68
- PLEASE DO NOT COPY-AND-PASTE NEVER EVER ;-)
- avoid going to database (TripDao)
- another example of seam: escaping singleton, static calls and object creation
- 100% coverage except seams (dependecies)
- builders (when we need to build rich object graph)
- After: ca5a502
As a code-base grows...
- Understanding decreases
- Impact on change increases
- Costs increase
Refactoring helps
- Clean up code
- Improve understanding
- Minimize impacts of change
- Increase Maintainability
- Reduce Costs
- Identify Code Smells
- Perform Impact Analysis
- Write a Unit Test (sometimes an Integration Test - better than nothing)
- Refactor
Starting from the deepest branch to the shortest (different than testing)
- Before: ca5a502
- method does too much (feature envy)
- Single Responsibility Principle!
- Tip for Mac keyboard: Alt+Insert == fn + alt/option + return
- ShouldInformWhenUsersAre(Not)Friends
- Resharper: Ctrl+Alt+F -> file structure
- Resharper: Shift+Alt+Space -> Import symbol completion
- stay green all the time!
- Pit stop: e36719c
- bring variables together (near usage)
- guard clause to the top
- get rid of variables (if you can) -> they make for complexity
- code should be read top-down
- always baby steps - bit by bit
- After: 8f7dfed
- Before: 8f7dfed
- it has dependecy on web framework
- static call (and dependency on data source)
- Pit stop: 97510e2
- test for retrieving trips from in memory db
- removing static method
- interface segregation
- service locator pattern and mocking dependecies
- get rid of Testable... (ugly) - protected virtual methods only for tests
- compare before and after - it could take 20 minutes (after some practice ;)
- After: 3c5b397
- Write readable and maintainable code
- code must express business rules
- Strive for simplicity
- Know your tools well (i.e. frameworks, editor)
- Work in small and safe increments
- commit often
- Embrace change, be brave
- Boy scout rule / No broken windows
- Working Effectively with Legacy Code
- Refactoring: Improving the Design of Existing Code
- Clean Code: A Handbook of Agile Software Craftsmanship
- NCrunch: best automatic continuous testing tool
- Mighty-Moose: free continuous testing tool
- Resharper
- VsVim - VIM emulation for Visual Studio
- Boost Test Library: old versions support Visual Studio 6.0!
- Google C++ Testing Framework
- Google Test (GTest) setup with Microsoft Visual Studio for C++ unit testing