legacy dependency killer - utah code camp 2014

28
LEGACY DEPENDENCY KILLER WILLIAM MUNN, CGFWB - @DUBMUN

Upload: dubmun

Post on 11-May-2015

661 views

Category:

Technology


1 download

DESCRIPTION

Legacy Dependency Killer is a hand-on coding session I lead at Utah Code Camp in March 2014. The focus is on refactoring for unit testing. The seed code is available on Github in C# and there are plans to provide translations in other languages. https://github.com/KatasForLegacyCode

TRANSCRIPT

Page 1: Legacy Dependency Killer - Utah Code Camp 2014

LEGACY DEPENDENCY

KILLERWILLIAM MUNN, CGFWB - @DUBMUN

Page 2: Legacy Dependency Killer - Utah Code Camp 2014

SESSION DETAILS• 10-15 MINUTES OF BACKGROUND • 50-55 MINUTES OF HANDS-ON

CODING

Page 3: Legacy Dependency Killer - Utah Code Camp 2014

SESSION DETAILS: YOU NEED THIS STUFF• SEED CODE IS WRITTEN IN C#

• GET IT FROM GITHUB: https://github.com/KatasForLegacyCode/kCSharp/archive/Step0.zip

• OR FROM ONE OF THE FLASH DRIVES:

kCSharp-Step0.zip

• ANY VERSION OF VISUAL STUDIO 2012/2013 THAT CAN RUN CONSOLE APPS AND UNIT TESTS

• AN NUNIT TEST RUNNER OR THE ABILITY TO QUICKLY SWITCH OUT NUNIT REFERENCE FOR MSTEST. I RECOMMEND NCRUNCH.

• WHEN WE ARE DONE CODING, I’M HOPING WE WILL HAVE 5-10 MINUTES FOR Q&A

• THE FIRST 5 SUGGESTIONS FOR ADDITIONS OR IMPROVEMENTS TO THIS SESSION/CODE WILL WIN THE FLASH DRIVES

Page 4: Legacy Dependency Killer - Utah Code Camp 2014

LEGACY

EXPECTATION REALITY

Page 5: Legacy Dependency Killer - Utah Code Camp 2014

LEGACY

PERCEPTION - COBOL

Page 6: Legacy Dependency Killer - Utah Code Camp 2014

LEGACY

PERCEPTION - COBOLREALITY – CODE W/O UNIT TESTS

Page 7: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY

EXPECTATION REALITY

Page 8: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY – THE HORRID TRUTH

Page 9: Legacy Dependency Killer - Utah Code Camp 2014

KILLER

EXPECTATION

Page 10: Legacy Dependency Killer - Utah Code Camp 2014

I’VE COMPLETELY SLAUGHTERED ALL THE LEGACY DEPENDENCIES

IN OUR CODEBASE. NOW I’LL ENJOY SOME LUNCH.

Page 11: Legacy Dependency Killer - Utah Code Camp 2014

KILLER

EXPECTATION REALITY

Page 12: Legacy Dependency Killer - Utah Code Camp 2014

PUT IT ALL TOGETHER & WHADDAYA GET•A CODE KATA• A CODE KATA IS AN EXERCISE IN

PROGRAMMING WHICH HELPS A PROGRAMMER HONE THEIR SKILLS THROUGH PRACTICE AND REPETITION

• THE WORD KATA IS TAKEN FROM JAPANESE ARTS MOST TRADITIONALLY MARTIAL ARTS

• WHY? BECAUSE HUMANS LEARN BY DOING

Page 13: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA

• DEPENDENCY KATA: CODE CAMP EDITION

• WRITTEN IN C#

• NOW WITH LESS TOOL DEPENDENCIES AND RESTRUCTURED TO WORK WITHOUT THEM

• ORIGINAL SOURCE CODE AVAILABLE ON GITHUB AT HTTPS://GITHUB.COM/DUBMUN/DEPENDENCYKATA

Page 14: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: INITIAL STRUCTURE

Page 15: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: FIRST THINGS FIRST

• LET’S START BY GETTING THE EXISTING TEST RUNNING

• RUNNING THE INTEGRATION TEST SHOWS THAT IT HANGS

• WE NEED TO BEGIN BY ABSTRACTING AND BREAKING THE DEPENDENCY ON Console.Readline()

• CREATE AN INTERFACE FIRSTpublic interface IConsoleAdapter{ string GetInput();}

• CREATE A CLASS THAT IMPLEMENTS THE INTERFACE

public class ConsoleAdapter : IConsoleAdapter

Page 16: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: FIRST THINGS FIRST• IMPLEMENT METHOD TO HANDLE THE

DEPENDENCYpublic class ConsoleAdapter : IConsoleAdapter{ public string GetInput() { return Console.ReadLine(); }}

• CREATE NEW CONSTRUCTOR FOR DOITALL THAT ACCEPTS ICONSOLEADAPTER AND SET A PRIVATE VARIABLE

private IConsoleAdapter _consoleAdapter;

public doItAll(IConsoleAdapter consoleAdapter){ _consoleAdapter = consoleAdapter;}

• THEN REPLACE 6 CALLS TO Console.ReadLine() WITH _consoleAdapter.GetInput()

Page 17: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: FIRST THINGS FIRST

• NOW WE HAVE SOME BROKEN INSTANTIATIONS

• IN THE CONSOLE APP INSTANTIATE AND PASS IN OUR NEW HANDLER

var doItAll = new DoItAll(new ConsoleAdapter());

• IN THE INTEGRATION TEST WE NEED TO DO SOMETHING DIFFERENT OR OUR TEST WILL STILL FAIL.

• CREATE A NEW IMPLEMENTATION OF IConsoleAdaper

public class FakeConsoleAdapter : IConsoleAdapter{ public string GetInput() { return string.Empty; }}

Page 18: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: FIRST THINGS FIRST

• WHEN THE TEST IS RUN WE NOW GET A MEANINGFUL EXCEPTION. ALL OF THE DEPENDENCIES THAT CAUSED THE HANG ARE GONE.

• THE TEST IS NOW FAILING BECAUSE THE PASSWORD IS EMPTY. THIS IS A MEANINGFUL CASE BUT LET’S JUST UPDATE OUR FAKE FOR NOW.

return “fakeInput”;

• THE TEST SHOULD BE GREEN NOW!

Page 19: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: FIRST THINGS FIRST

• WHEN THE TEST IS RUN WE NOW GET A MEANINGFUL EXCEPTION. ALL OF THE DEPENDENCIES THAT CAUSED THE HANG ARE GONE.

• THE TEST IS NOW FAILING BECAUSE THE PASSWORD IS EMPTY. THIS IS A MEANINGFUL CASE BUT LET’S JUST UPDATE OUR FAKE FOR NOW.

return “fakeInput”;

• THE TEST SHOULD BE GREEN NOW!

• LET’S ADD AN ASSERT FOR GOOD MEASURE[Test, Category("Integration")]public void DoItAll_Does_ItAll(){ var doItAll = new DoItAll(new FakeConsoleAdapter()); Assert.DoesNotThrow(() => doItAll.Do());}

Page 20: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: BETTER COVERAGE

• WE DON'T HAVE COVERAGE SOME OF THE CODE STILL AND NO QUANTIFIABLE RESULTS TO TEST

• LET’S COPY OUR EXISTING TEST AND RENAME IT DoItAll_Fails_ToWriteToDB

• THEN CHANGE THE ASSERT StringAssert.Contains(

"Database.SaveToLog Exception:", doItAll.Do());

• THIS WILL FAIL TO BUILD BECAUSE OUR METHOD IS CURRENTLY VOID. LET’S CHANGE THAT

• CHANGE DO()’S RETURN TYPE TO STRING

• ADD A RETURN STATEMENT IN 2 LOCATIONS:• AT THE END OF THE USING STATEMENT

• AT THE END OF THE METHOD

• NOW CREATE VARIABLES TO HOLD THE MESSAGES AT VARIOUS POINTS FOR RETURN

private const string _passwordsDontMatch =

"The passwords don't match.";

Page 21: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: BETTER COVERAGE

• NOW CREATE VARIABLES TO HOLD THE MESSAGES AT VARIOUS POINTS FOR RETURN

private const string _passwordsDontMatch =

"The passwords don't match."; ANDvar errorMessage = string.Format(

"{0} - Database.SaveToLog Exception: \r\n{1}", message, ex.Message);

• RETURN THOSE VALUES AS APPROPRIATE

• OUR NEW TEST SHOULD NOW PASS

Page 22: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: BETTER ABSTRACTION• DO WORK TO ABSTRACT CONSOLE

COMPLETELY COMPLETELY

• ADD A NEW METHOD STUB TO IConsoleAdapter:

Void SetOutput(string output);

• UPDATE ConsoleAdapter IMPLEMENTATIONpublic void SetOutput(string output){ Console.WriteLine(output);}

• UPDATE FakeConsoleAdapter IMPLEMENTATION

public void SetOutput(string output){}

• UPDATE DoItAll IMPLEMENTATION REPLACING 6 INSTANCES OF Console.WriteLine WITH _consoleAdapter.SetOutput

• OUR TESTS SHOULD STILL PASS

Page 23: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: REFACTOR

• DoItAll.Do() IS TRYING TO DO TOO MUCH

• EXTRACT LOGGING FUNCTIONALITY BY CREATING A NEW INTERFACE

public interface ILogger{ string LogMessage(string message);}

• NOW EXTRACT THE CODE IN THE TRY/CATCH BLOCK IN DoItAll.Do() INTO THE IMPLEMENTATION OF ILogger.LogMessage()

• MAKE SURE ALL PATHS RETURN A MESSAGE

• NOW UPDATE THE CONSTRUCTOR OF DoItAll WITH AN ILogger SAND REPLACE TRY/CATCH:

message = _logger.LogMessage(message);

Page 24: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: REFACTOR

• CLIENT NO LONGER BUILDS BECAUSE OF THE UPDATED CONSTRUCTOR SO UPDATE IT

• NOW CREATE A NEW FAKE FOR TESTING:public class FakeLogger : ILogger{ public string LogMessage(

string message) { return string.Empty; }}

• UPDATE THE DoItAll MINSTANTIATIONS IN THE TESTS TO INCLUDE new FakeLogger()

• THE FIRST TEST PASSES BUT THE OTHER FAILS BECAUSE IT DEPENDS ON IMPLEMENTATION-SPECIFIC DETAILS

Page 25: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: REFACTOR

• COPY THE SECOND TEST AND RENAME THE COPY DoItAll_Succeeds_WithMockLogging

• UPDATE THE ASSERT:Assert.AreEqual(

string.Empty, doItAll.Do());

• TEST WILL PASS

• LET THE FORMER TEST DEPEND ON DatabaseLogger AND IT WILL PASS AS WELL

Page 26: Legacy Dependency Killer - Utah Code Camp 2014

DEPENDENCY KATA: WHAT’S NEXT?

• THERE ARE STILL PLENTY OF THING ABOUT THIS LEGACY CODE I WOULD CHANGE

• WHAT WOULD YOU DO NEXT?

Page 27: Legacy Dependency Killer - Utah Code Camp 2014

THANKS TO OUR SPONSORS!

To connect to wireless 1. Choose Uguest in the wireless list

2. Open a browser. This will open a Uof U website 3. Choose Login

Page 28: Legacy Dependency Killer - Utah Code Camp 2014

WILLIAM MUNN, CGFWB

TWITTER - @DUBMUN

GITHUB – DUBMUN

BLOG – HTTP://BLOG.DUBMUN.COM

RESOURCES

WORKING EFFECTIVELY WITH LEGACY CODE, MICHAEL FEATHERS

THE ART OF UNIT TESTING, ROY OSHEROVE

CLEAN CODE, ROBERT C. MARTIN

PRAGMATIC PROGRAMMER, DAVE THOMAS & ANDY HUNT

LEGACY DEPENDENCY KILLER – UTAH CODE CAMP

2014