Modernising Legacy Code

554 views

Published on

Modernising legacy code, through applying unit tests and refactoring

Published in: Technology, Education
  • Be the first to comment

Modernising Legacy Code

  1. 1. Modernising Legacy Code Sam Harwood
  2. 2. What is Legacy Code?OldFragileIncomprehensibleNot under test
  3. 3. Why Modernise Legacy Code?New developmentBug fixesChange requestsTo reduce maintenance costs
  4. 4. When to Refactor Legacy CodeTheres no smoke...Its not safe to refactorKey element of applicationMy boss told me not to
  5. 5. An Iterative ApproachStart smallIdentify target areasDeploy, deploy, deploy!
  6. 6. Process PlanBreak dependencies with minimal changes; apply testsMake small changesClean up testsRefactor to clean code
  7. 7. Untestable BehaviourDB connectivityWeb servicesGlobal varsexit();File workSystem callsecho(), print(), var_dump(), …Memcache etc.
  8. 8. How to Break DependenciesPartial mockeryDependency injectionOut-of-context testing
  9. 9. Option 1: Partial Mockerypublic function __construct() { $dbh = new DbConnection(localhost, db, user, pass, 3306); if ($dbh->connection_fail) {exit;} if ($GLOBALS[debug mode]) { print Constructing!; } $this->doOtherThings();}
  10. 10. Partial Mocks cont.public function __construct() { $dbh = $this->_getDbConnection(); //...}/** * @return DbConnection */protected function _getDbConnection() { return new DbConnection(localhost, db, user, pass, 3306);}
  11. 11. Partial Mocks cont.public function setUp() { $this->_objectUnderTest = $this->getMock(MyClass, array(_getDbConnection, _doExit, _sendHttpQuery), array(), , , true); $this->_mockDbConnection = $this->getMock(DbConnection); $this->_objectUnderTest->expects($this->any()) ->method(_getDbConnection) ->will($this->returnValue($this-> _mockDbConnection));}
  12. 12. Option 2: Dependency Injectionpublic function __construct() { $dbh = $this->_yadifContainer->getComponent(DbConnection); //...}
  13. 13. Option 3: Out-of-Context Testing Fake objects set_include_path(); runkit.internal_override & runkit_function_redefine()
  14. 14. Next StepsProvide satisfactory test coverage, and refactor...Add tests for constructorComment code to help plan testsCover other code addressed in this iteration
  15. 15. Add Comments...public function massiveMethod($param = hello world) { // Check if DB knows how awesome we are $dbConnection = $this->_getDbConnection(); $resultSet = $dbConnection->executeSql( SELECT group_of_developers FROM london WHERE everyone_is LIKE "%were the best php%" GROUP BY miles); $dbRes = count($resultSet) > 0; // Calculate something else $aString = $param; $aString .= more string; $aString .= more string; $aString .= more string; $aString .= more string;
  16. 16. ...Remove Commentspublic function massiveMethod($param = hello world) { $dbRes = $this->_checkIfDbKnowsHowAwesomeWeAre(); $aString = $this->_generateSomeString($param);
  17. 17. Things to Bear in Mind WhenBreaking Up Large FunctionsMethod name reflects commentTest section before moving itParameter count
  18. 18. Looking Inside Large MethodsSensing variables if () { foreach () { if () { $this->_privateAttrib = something; $this->sensingVariable = true; } } }Indecent exposurePartially mock most of class
  19. 19. Cleaning Up Production CodeMinimise public APIRemove unnecessary commentsRoll out dependency injectionBe sure to remove methods for partial mocking
  20. 20. Refactoring TestsInject mock objects via DIStop testing partial mockRemove out-of-context setupRemove single-line constructor tests, sensing variable tests etc.
  21. 21. SummaryBreak dependenciesApply testsRefactor production codeRefactor tests
  22. 22. samthephpdev@gmail.com @AgileTillIDie

×