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
3. SESSION DETAILS: YOU NEED THIS STUFF
• SEED CODE IS WRITTEN IN C#
• GET IT FROM GITHUB:
https://github.com/KatasForLegacy
Code/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
12. 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
13. 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
15. 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 FIRST
public interface IConsoleAdapter
{
string GetInput();
}
• CREATE A CLASS THAT IMPLEMENTS THE
INTERFACE
public class ConsoleAdapter :
IConsoleAdapter
16. DEPENDENCY KATA: FIRST THINGS FIRST
• IMPLEMENT METHOD TO HANDLE THE DEPENDENCY
public 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()
17. 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;
}
}
18. 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!
19. 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());
}
20. 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.";
21. 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.";
AND
var errorMessage = string.Format(
"{0} - Database.SaveToLog
Exception: rn{1}",
message, ex.Message);
• RETURN THOSE VALUES AS APPROPRIATE
• OUR NEW TEST SHOULD NOW PASS
22. DEPENDENCY KATA: BETTER ABSTRACTION
• DO WORK TO ABSTRACT CONSOLE COMPLETELY
COMPLETELY
• ADD A NEW METHOD STUB TO
IConsoleAdapter:
Void SetOutput(string output);
• UPDATE ConsoleAdapter IMPLEMENTATION
public 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
23. 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);
24. 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
25. 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
26. DEPENDENCY KATA: WHAT’S NEXT?
• THERE ARE STILL PLENTY OF THING ABOUT THIS LEGACY CODE I WOULD CHANGE
• WHAT WOULD YOU DO NEXT?
27. 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