void buildCars(int number, String model, String brand) { for (int i = 0; i < number; i++) { String serialNumber; // // 20 lines of code to calculate new serial number // Car car = new Car(model, brand, serialNumber); Car.save(car); // triggers write to prod db } } } new CarDAO().save(car); // same problem Example thanks to http://www.wakaleo.com/component/content/article/240
dao = new DefaultCarDAO(); public void setDao(CarDAO dao) { this.dao = dao; } public void buildCars(int number, String model, String brand) { for (int i = 0; i < number; i++) { String serialNumber = generateSerialNumber(model, brand); Car car = new Car(model, brand, serialNumber); dao.save (car); } } }
void carFactoryShouldGenerateTheRightSerialNumber() { CarFactory factory = new CarFactory(); CarDao mockDao = mock(CarDao.class); factory.setDao(mockDao); // I know what the serial number should be String expectedSerialNumber = "123456"; Car aToyotaPrius = new Car("Toyota", "Prius", expectedSerialNumber); factory.buildCars(1, "Toyota", "Prius"); verify(mockDao).save(refEq(aToyotaPrius))); } } // Though might be better to test (and code) serial number generation in isolation...
suspects: • Long, excessively complex methods • Missing abstractions (manifested as code-level duplication) • Rampant interdependency • Generally, poor choice of units ! Given poor units, difficult to derive meaningful unit tests (prior to change) ! May be wise to focus on mini integration tests first
lock existing behaviour into an untested or undocumented system. ! It may be impossible to know the "correct" behaviour or safe way to refactor when making changes to or around legacy code that has no tests. This is especially true when no specification is available, or when other code may rely on flaws in the code. ! In these situations, it is typically best to assume that the old behaviour must be preserved unless explicit changes are required. ! In order to preserve the old behaviour, characterization tests are written that use the system as it has already been used. http://c2.com/cgi/wiki?CharacterizationTest
of code again for years (unlikely) It’s likely code will be read periodically regardless of whether or not it gets changed Likely case: changes cluster, code changed today is likely to be changed again soon
systems landscape ! Carelessly, the original developers all died in a plane crash ! Safe to edit (refactor) – not a public library ! Some progress already made on characterization tests ! Pressing issues: Ø Add support for H7 Ø Pull definition of known tags (like H7) out of HtmlTag and into a separate class (intent, later, is to load these from config) Ø HtmlException is a pain. We want to switch to using (well-chosen) subclasses of RuntimeException, but currently have little or no understanding of various scenarios where a HtmlException is thrown
! Robert C. Martin, Clean Code ! Martin Fowler, Refactoring: Improving the Design of Existing Code ! Chris Stevenson and Andy Pols, An Agile Approach to a Legacy System ! Dan North, Pimp My Architecture (http://www.infoq.com/ presentations/north-pimp-my-architecture)