Legacy Dependency Killer - Utah Code Camp 2014
-
Upload
dubmun -
Category
Technology
-
view
661 -
download
1
description
Transcript of Legacy Dependency Killer - Utah Code Camp 2014
LEGACY DEPENDENCY
KILLERWILLIAM MUNN, CGFWB - @DUBMUN
SESSION DETAILS• 10-15 MINUTES OF BACKGROUND • 50-55 MINUTES OF HANDS-ON
CODING
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
LEGACY
EXPECTATION REALITY
LEGACY
PERCEPTION - COBOL
LEGACY
PERCEPTION - COBOLREALITY – CODE W/O UNIT TESTS
DEPENDENCY
EXPECTATION REALITY
DEPENDENCY – THE HORRID TRUTH
KILLER
EXPECTATION
“
”
I’VE COMPLETELY SLAUGHTERED ALL THE LEGACY DEPENDENCIES
IN OUR CODEBASE. NOW I’LL ENJOY SOME LUNCH.
KILLER
EXPECTATION REALITY
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
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
DEPENDENCY KATA: INITIAL STRUCTURE
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
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()
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; }}
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!
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());}
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.";
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
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
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);
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
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
DEPENDENCY KATA: WHAT’S NEXT?
• THERE ARE STILL PLENTY OF THING ABOUT THIS LEGACY CODE I WOULD CHANGE
• WHAT WOULD YOU DO NEXT?
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
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