dev411 testing un-testable code with visual studio 2012 fakes peter provost sr. program manager lead...

30

Upload: daisy-jordan

Post on 02-Jan-2016

220 views

Category:

Documents


0 download

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

Removing external dependencies byintroducing interface dependencies

Refactoring to Interfaces

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

Unit Testing code that has interface dependencies

Visual Studio 2012 Stubs

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?

Isolating your code from external dependencies the brute force way

Visual Studio 2012 Shims

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.