dev411 testing un-testable code with visual studio 2012 fakes peter provost sr. program manager lead...
TRANSCRIPT
DEV411
Testing Un-Testable Code with Visual Studio 2012 FakesPeter ProvostSr. Program Manager LeadMicrosoft Corporation
DEV411
Warning!
This is a 400 level deep dive on Visual Studio 2012 Fakes!
AssumptionsYou are comfortable with .NET and Unit TestingYou already understand the basics of mocks, stubs, etc.You believe that unit testing has significant benefits
And…You want to see code and real problemsYou want to have fun and are willingto ask questions
!
Effective unit testing is one of the biggestcontributors to high quality software.
Testing legacy code is hard, and sometimes impossible.
Or is it?
What is un-testable code?
Where do we find it?
“Get ‘er done” codeBusiness logic in code-behindClasses with too many dependenciesDatabase logic mixed with business logic
Testability was not a considerationOr was explicitly avoided
Monolithic, highly-coupled designs
Common indicators
Complex test setup and teardownEnvironmental dependenciesPublic static methodsHidden object creationComplex external frameworksNo tests at all!
Any system where the tests require complex setup or where the tests run very slowly is basically untestable
void FileExistsTest() { File.Write("foo.txt", ""); var result = IsFileEmpty("foo.txt") Assert.IsTrue(result);}
File.Write("foo.txt", "");
A simple test setup example
The method we want to unit test
bool IsFileEmpty(string file) { var content = File.ReadAllText(file); return content.Length == 0;}
File.ReadAllText(file);
Is this a “good” test?…this ugly setup code
This dependency forces…
Environmental Dependencies
Consider the following Y2K code:
public void ThrowIfEndOfTheWorld() { if (DateTime.Now == new DateTime(2000,1,1)) throw new Y2KBugException();}
How would you test it reliably?
[DllImport("kernel32.dll")]extern static bool SetSystemTime(ref SystemTime time);
[TestMethod]public void Y2KTest(){ SetSystemTime(2000,1,1,0,0,0); Assert.Throws( () => ThrowIfEndOfTheWorld() );}
Environmental Dependencies
How about this? Why is this bad?
Isolation is critical
When you can’t isolateSlow tests with complicated setupNon-deterministic resultsMay be totally untestable
Two choicesTestable design
Abstraction layers, interfaces, dependency injection, stubs and overrides
Un-testable designNo abstraction layers, lots of static methods, sealed typed, etc.
SOLID Principles
S
O
L
I
D
Open/closed PrincipleA module should be open for extension but closed for modification.
Liskov Substitutability PrincipleSubclasses should be substitutable for their base classes.
Interface Segregation PrincipleMany client specific interfaces are better than one general purpose interface.Dependency Inversion PrincipleDepend upon Abstractions. Do not depend upon concretions.
Single ResponsibilityThere should never be more than one reason for a class to change.
Refactoring to Interfaces
Replace external calls with interfacesWrap external APIsInterface exposes only what you consumeClass expresses requirements via interface dependencies
Useful patternsNull ObjectDefault ImplementationDependency Injection / Inversion of Control
Review: Refactoring to Interfaces
The good partsThe code expresses its requirementsVery loosely coupled to external things
Free gift: Easier to replace external APIs later
Higher cohesion in you classes When done right
But there are dangersDependency AddictionType explosionCan be expensive and disruptive to the code
Unit Testing with Interface Dependencies
Stubs and MocksProvide easy to use, concrete implementations for your testingSimilar but different in their approach
Visual Studio 2012 StubsGenerated at compile time (runs fast)Uses C# language features (no special API)Type compatible (is-a) with the Interface
Review: Visual Studio 2012 Stubs
Concrete derivatives of interfaces and non-sealed classes
Generated at compile timeRun very fast
Uses standard language notationsNo special API to learnDelegates provide implementationConstructor initializers to organizeyour delegate overrides
Visual Studio
StubS
What about when you can’t refactor?
Framework IssuesIf your class derives from an untestable, concrete type that has complex internal setupOr a complex graphs of objects must be setup properly before you can create and test your object
ExpediencyLots of legacy code and no timeOrganizational design prohibitions
The Catch-22 of Refactoring to Interfaces
Refactoring defined:A disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
—Martin Fowler, http://refactoring.com
When code doesn’t have unit tests, how do you know if you changed the external behavior?
Review: Visual Studio 2012 Shims
Runtime interception of any .NET methodUses the profiler to detour calls“Monkey patching” for .NET
Use it when you…Have external components that cannot be refactored
SharePoint, ASP.NET, Office, etc.
Need to override non-virtual methodsStatic methods, constructors, sealed types, properties
Have legacy code with untestable designs
The Catch-22 Revisited
When you need to refactor to interfacesFirst you must define the original behavior with unit testsYou can use Shims to provide the test coverageBut don’t stop there!!
Once you have the coverageDo the refactoringAdd new tests using stubs to verify the behaviorKeep the original test runningWhen you’re done, you can remove the shims test
Tips & Tricks
Configure the Fakes compiler to omit things you don’t useUse closures to collect data from delegate methodsYou can add missing System.* types via configuration
This can be dangerous!Declare your ShimsContext within each test
Not doing this is dangerous!Get rid of the Shims-based tests as soon as you can
Much slower to execute than StubsDepend on internal knowledge of your system
Configuring, troubleshooting and how to not delete your hard drive
Visual Studio 2012 FakesTips and Tricks
SOLID Principles (redux)
S
O
L
I
D
Open/closed PrincipleA module should be open for extension but closed for modification.
Liskov Substitutability PrincipleSubclasses should be substitutable for their base classes.
Interface Segregation PrincipleMany client specific interfaces are better than one general purpose interface.Dependency Inversion PrincipleDepend upon Abstractions. Do not depend upon concretions.
Single ResponsibilityThere should never be more than one reason for a class to change.
Effective unit testing is one of the biggestcontributors to high quality software.
Testing legacy code is hard, but not impossible.
Then you can use the tests to improve your design.
Thank you!
Have questions now?Please use the micsI will stick around outside after
Think of a question [email protected]@pprovost
Peter Provost
Find Me Later At DEV01-TLC: Application Lifecycle Management (ALM)
Related Content
Breakout SessionsDEV214 Introducing the New Visual Studio 11 Unit Testing ExperienceAAP401 Real World Developer Testing with Visual Studio 2012DEV411 Testing Un-testable Code with Fakes in Visual Studio 2012AAP330 Compile & Execute Requirements in .NETHands on LabsDEV17-HOL Explore the New Unit Testing and Code Clone Capabilities of
Visual Studio 2012Product Demo Stations
DEV01-TLC Application Lifecycle Management (ALM)
Resources
Connect. Share. Discuss.
http://europe.msteched.com
Learning
Microsoft Certification & Training Resources
www.microsoft.com/learning
TechNet
Resources for IT Professionals
http://microsoft.com/technet
Resources for Developers
http://microsoft.com/msdn
Evaluations
http://europe.msteched.com/sessions
Submit your evals online
© 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to
be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS
PRESENTATION.