You are on page 1of 27

© HaroldoPalo, Jr

Knowledge Sharing – Quality Control


Recent advances in unit testing and mocking
Agenda

• Define a unit test


• Make a case for unit testing as our top priority
• Strategies for implementing unit tests
– “Gang of Four” design patterns
– Legacy code refactorings
• Introduction to mocking with Rhino Mocks
– Dummies, stubs, mocks
– State vs. behavior verification
• NUnit parameterized tests
• FxCop
Unit Testing Definition

• From Wikipedia:
– a software verification and validation method in which a
programmer tests that individual units of source code are fit for
use
– a unit is the smallest testable part of an application
– in object-oriented programming, the smallest unit is a class,
which may belong to a base/super class, abstract class or
derived/child class

• (Fowler) Not a unit test if:


– It talks to a database
– It communicates across a network
– It touches the file system
– It requires special manipulation of the environment to run
What is a good unit test?
(Learning Tree 511 class)

• Simple
– A programmed check of the result against the
expected result
• Independent
– does not depend on other tests
• Fast
– Can be run over and over in less than 1/100 of a
second
– Run tests frequently to immediately catch coding
errors
• A client of the public interface
– a test verifies the externally observable behavior of
the class under test
What is a good unit test?
(Osherove)

A unit test should have the following properties:


• It should be automated and repeatable.
• It should be easy to implement.
• Once it’s written, it should remain for future use.
• Anyone should be able to run it.
• It should run at the push of a button.
• It should run quickly.
A case for unit testing
(Fowler)

“Unit testing is one of the most important


components in legacy code work. System-
level regression tests are great, but small,
localized tests are invaluable. They can
give you feedback as you develop and
allow you to refactor with much more
safety.”

Unit tests allow for safe refactoring, because


any refactoring should immediately either
pass or fail existing unit tests
A case for unit testing, cont’d.

(Osherove) Unit testing “proves itself time and time


again as one of the best ways a developer can
improve code quality while gaining a deeper
understanding of the functional requirements of
a class or method.”

My personal experience, as I have become more


comfortable and sophisticated with unit testing,
is that I am forced to write better code by making
it unit-testable.
Disadvantages of Regression Tests
(Fowler)

• Regression tests are written after development


by testers
– Problem: feedback loop takes a long time

• Regression testing traditionally done at the


application level
– Problem: tests are unable to determine who broke
code and where it is broken because tests are too
coarse-grained
Disadvantages of large functional (integration) tests
(Fowler)

• Error localization
– Hard to determine what a test failure means
– Can take considerable work to pinpoint source of test
fail
• Execution time
– Larger tests take longer to execute
– Tests that take too long are not run frequently enough
• Coverage
– Hard to directly correlate large tests to class methods
– When new code is added, hard to modify large tests
to exercise the new code
Example of the problem with large functional tests

Functional test reveals that Shapefile


Upload fails with an exception.
• Did the zip file not upload properly (e.g.,
not saved to server)?
• Did the unzip fail?
• Did the shape not get saved to SDE? If
so, was it a connection failure? ArcObjects
issue?
• Etc.
Strategies to help with unit testing

“Gang of Four” design patterns can be key to


successful unit testing
• Strategy Pattern
– Program to an interface, not an implementation
– Allows you to mock the interface for testing
• Adapter Pattern
– Adapt 3rd party libraries that otherwise would be
untestable
– Once adapted, now can use techniques similar to
Strategy Pattern (program to interface; can mock the
interface)
Strategy Pattern example

Card game example from


Learning Tree 511 class GameHand IHandEvaluator
Valuation
Value(IHandEvaluator) Value(GameHand)
strategy

•Gamehand.Value
function receives the
strategy object that tells MockEvaluator
it how to calculate the Value(GameHand)
value
•It “calls back” into that
object to use the
valuation logic
BridgeHandEvaluator PokerHandEvaluator
•GameHand class is
decoupled from the Value(GameHand) Value(GameHand)
algorithm for calculating
the value
Adapter Pattern example

Problem: Can’t unit test a MapResourceManager (ESRI ArcObjects).


MapResourceManager cannot be instantiated in a unit testing environment.

Solution: Create an adapter that implements an interface. The interface can be


mocked for unit testing.
public class MapResourceManagerAdapter : IMapResourceManagerAdapter
{
private MapResourceManager _mrm;
public MapResourceManagerAdapter(MapResourceManager mrm)
{
if (mrm == null)
throw new ArgumentNullException("mrm");
_mrm = mrm;
}
public int Add(IMapResourceItemAdapter adapter)
{
try
{
MapResourceItem item = ((MapResourceItemAdapter)adapter).GetMapResourceItem();
return _mrm.ResourceItems.Add(item);
}
catch { throw; }
}
public int IndexOf(string name)
{
try
{
return _mrm.ResourceItems.IndexOf(_mrm.ResourceItems.Find(name));
}
catch { throw; }
}
}
Refactoring strategies for unit testing

“Working Effectively with Legacy Code”


book is full of refactoring strategies to get
legacy code under test.
These strategies can be used while
developing code for situations where true
TDD is not being used.
“Working Effectively with Legacy Code” Highlights

• Chapter 4 “The Seam Model”


– How to look at existing code to break dependencies
– “A seam is a place where you can alter behavior in
your code without editing in that place.”
• Chapter 25 “Dependency-Breaking Techniques”
– Strategies for decoupling classes well enough to get
them under test
• Part II – Changing Software
– FAQ style collection of chapters with specific
refactorings and techniques
Types of Unit Tests
(Fowler)

Unit Tests fall into 2 general categories. Critical to


test in both categories for most systems under
test.
• State Tests
– State = all values stored in an object at any given
moment
– Test the state of a class for correctness
• Behavior Verification Tests
– Behavior = object methods
– Ensure that methods are called in the correct
sequence and return the correct results
Using a mock object library

Mock object libraries can greatly facilitate the unit testing


process
Fowler defines 4 types of mock objects:
• Dummy objects are passed around but never actually used. Usually
they are just used to fill parameter lists.
• Fake objects actually have working implementations, but usually
take some shortcut which makes them not suitable for production
(an in memory database is a good example).
• Stubs provide canned answers to calls made during the test,
usually not responding at all to anything outside what's programmed
in for the test.
• Mocks are objects pre-programmed with expectations which form a
specification of the calls they are expected to receive.
Dummy example
(Rhino Mocks and NUnit)

public MapResourceManagerInitializer(IMapResourceManagerAdapter mrm,


IMapResourceItemFactory mriFactory, IErrorLogger logger)
{
if (mrm == null) throw new ArgumentNullException("mrm");
if (mriFactory == null) throw new ArgumentNullException("mriFactory");
if (logger == null) throw new ArgumentNullException("logger");
_mrm = mrm;
_mriFactory = mriFactory; All null tests pass because
_logger = logger;
}
the dummy objects are
not null
[Test]
[Description("Test normal instantiation with valid parameters provided to the
constructor.")]
public void Creation()
{
IMapResourceManagerAdapter mrmDummy =
MockRepository.GenerateStub<IMapResourceManagerAdapter>();
IMapResourceItemFactory mriDummy =
MockRepository.GenerateStub<IMapResourceItemFactory>();
IErrorLogger logDummy =
MockRepository.GenerateStub<IErrorLogger>();
MapResourceManagerInitializer obj =
new MapResourceManagerInitializer(mrmDummy, mriDummy, logDummy);
Assert.That(obj, Is.Not.Null);
}
Stub/Fake example
(Rhino Mocks and NUnit)

public bool InitializeAllResourceItems(Collection<LayerProperties> allLayers)


{ MapResourceManager stub
bool initialized = _mrm.InitializeMapResourceManager();
if (initialized) fails to initialize, returns
{
foreach (LayerProperties lp in allLayers)
false
{
IMapResourceItemAdapter item = _mriFactory.CreateMapResourceItem(lp);
if (MapResourceManagerInitializerHelper.InitializeResourceItem(_mrm, item, _logger) == false)
initialized = false;
}
}
else
_logger.LogStringToFile("Unable to initialize the MapResourceManager control",
TNC.ErrorHandlerAndLogging.Logging.LogErrorLevel.Error);
return initialized;
}

[Test]
[Description("Confirm InitializeAllResourceItems method returns false when the
IMapResourceManagerAdapter.InitializeMapResourceManager method fails")]
public void InitializeAllResourceItems_Returns_False_When_InitializeMapResourceManager_Fails()
{
IMapResourceItemFactory mriDummy = MockRepository.GenerateStub<IMapResourceItemFactory>();
ErrorLoggerMock logStub = new ErrorLoggerMock();
IMapResourceManagerAdapter mrmStub = MockRepository.GenerateStub<IMapResourceManagerAdapter>();
mrmStub.Expect(mrm => mrm.InitializeMapResourceManager()).Return(false);

MapResourceManagerInitializer obj = new MapResourceManagerInitializer(mrmStub, mriDummy, logStub);


Assert.That(obj.InitializeAllResourceItems(_lyrPropCollAll), Is.EqualTo(false));
Assert.That(logStub.StringsToFile.Count, Is.EqualTo(1));
Assert.That(logStub.StringsToFile[0], Is.EqualTo("Unable to initialize the
MapResourceManager control | Error"));
}
Mock example
(Rhino Mocks and NUnit parameterized test)

1. Mock factory makes


public bool InitializeAllResourceItems(Collection<LayerProperties> allLayers) LayerProperties that fail to
{
bool initialized = _mrm.InitializeMapResourceManager();
initialize
if (initialized) 2. InitializeResourceItem method
{ returns false
foreach (LayerProperties lp in allLayers)
{
IMapResourceItemAdapter item = _mriFactory.CreateMapResourceItem(lp);
if (MapResourceManagerInitializerHelper.InitializeResourceItem(_mrm, item, _logger) == false)
initialized = false;
}
}
else
_logger.LogStringToFile("Unable to initialize the MapResourceManager control",
TNC.ErrorHandlerAndLogging.Logging.LogErrorLevel.Error);
return initialized;
}

[TestCase(0, 4)]
[TestCase(1, 1)]
[Description("Confirm InitializeAllResourceItems method correctly add/removes MapResourceItems in the MapResourceManager when there
are Factory failures")]
public void
InitializeAllResourceItems_Correctly_Removes_MapResourceItems_From_MapResourceManager_For_IMapResourceItemFactory_Failures(int
failScenario, int failCount)
{
IErrorLogger logDummy = MockRepository.GenerateStub<IErrorLogger>();
MapResourceManagerAdapterMock mrmMock = new MapResourceManagerAdapterMock();
MapResourceItemFactoryMock mriMock = new MapResourceItemFactoryMock((MapResourceItemFactoryMock.FailScenario)failScenario);
MapResourceManagerInitializer obj = new MapResourceManagerInitializer(mrmMock, mriMock, logDummy);

obj.InitializeAllResourceItems(_lyrPropCollAll);
Assert.That(mrmMock.ResourceItemCount,
Is.EqualTo(_lyrPropCollAll.Count - failCount)); MapResourceManager mock counts
} the number of resources added
Rhino Mocks Example from Documentation
(http://ayende.com/Wiki/Rhino+Mocks+Ordered+and+Unordered.ashx)

[Test]
public void MovingFundsUsingTransactions()
{
MockRepository mocks = new MockRepository();
IDatabaseManager databaseManager = mocks.StrictMock<IDatabaseManager>();
IBankAccount accountOne = mocks.StrictMock<IBackAccount>(),
accountTwo = mocks.StrictMock<IBackAccount>();
using(mocks.Ordered()) This code verifies that the
{ transfer of funds from one
Expect.Call(databaseManager.BeginTransaction()) account to another is
.Return(databaseManager); wrapped in a transaction
using(mocks.Unordered())
{ The implementation is free
Expect.Call(accountOne.Withdraw(1000)); to withdraw from the first
Expect.Call(accountTwo.Deposit(1000)); account first, or to deposit
} into the second account
Expect.Call(databaseManager.Dispose()); first, both are legal, as long
} as both actions happen
mocks.ReplayAll();
Bank bank = new Bank(databaseManager);
bank.TransferFunds(accountOne,accountTwo,1000);
mocks.VerifyAll();
}
Tools

NUnit Test Runner


• Quickly runs all unit tests in an assembly at the click of a button
• Graphically shows which tests passed, failed, or are skipped
• Shows stack trace and meta-information
FxCop
• Analyzes managed code assemblies and reports information about
the assemblies, such as possible design, localization, performance,
and security improvements
• Provides suggested remedies
• Can set up custom rules, ignore canned rules, etc
Tool Demonstration: NUnit Test Runner
Tool Demonstration: FxCop
Recommendations

• Focus on unit testing for now


– Leverage Safari books (“unit test” search turned up
5161 titles) or other training resources
– Become familiar with xUnit framework (NUnit for
.NET, PyUnit for Python)
– Become familiar with mocking framework (Rhino
Mocks for .NET, PyMock (?) for Python)
– Incorporate test results from FxCop or equivalent
• Next steps:
– Continuous integration (e.g., CruiseControl); I hope to
learn enough at DevLink in Aug to start this
– Code coverage (e.g., NCover)
Citation References

Burton, Dennis. “PatternsInTesting[0] – Introduction.” Develop using .net. 2008.


<http://developusing.net/2008/12/04/PatternsInTesting0Introduction.aspx>

Burton, Dennis. “PatternsInTesting[1] – Dummy Pattern.” Develop using .net. 2008.


<http://developusing.net/2008/12/04/PatternsInTesting1DummyPattern.aspx>

Burton, Dennis. “PatternsInTesting[2] - Stub Pattern.” Develop using .net. 2008.


<http://developusing.net/2008/12/10/PatternsInTesting2StubPattern.aspx>

Feathers, Michael. Working Effectively with Legacy Code. New Jersey: Prentice Hall PTR, 2005.

Fowler, Martin. “Mocks aren’t Stubs.” MartinFowler.com. 2007.


<http://www.martinfowler.com/articles/mocksArentStubs.html>

Gamma, Helm, Johnson, and Vlissides. Elements of Reusable Object-Oriented Software.


Massachussetts: Addison-Wesley, 1995.

Osherove, Roy. The Art of Unit Testing with Examples in .NET. Connecticut: Manning
Publications Co, 2009.
Tool References

NUnit
– http://www.nunit.org
FxCop
– http://msdn.microsoft.com/en-
us/library/bb429476(VS.80).aspx (MSDN description)
– http://www.microsoft.com/downloads/details.aspx?fa
milyid=9AEAA970-F281-4FB0-ABA1-
D59D7ED09772&displaylang=en (download)
– http://www.codeplex.com (custom rules)

You might also like