TDD Introduction
by Peter Daukintis
Test Driven Development
This is a basic introduction to test-driven development describing related technologies in a simple, straightforward way. The learning curve can sometimes appear steep due to the range of associated technologies and architectural concepts that need to be understood. This post is a quick overview with some simple examples.
Basic Principles
TDD supports agile processes and coping with changing business requirements. It does this by enabling and promoting sound, flexible architectures which support unit testing. The test frameworks this allows, in turn, enables refactoring by providing confidence that existing functionality is not being broken. It supports a ‘clear separation of concerns’ via inversion of control patterns (http://www.martinfowler.com/articles/injection.html). This enables dependencies to be easily replaced, promotes flexible architectures and enables runtime composition of logical components.
Dependencies
public IEnumerable<Stuff> GetFilteredStuff(string filter) { DatabaseLayer dbl = new DatabaseLayer(); var stuff = dbl.GetStuffFromDB(); return stuff.Where(stuff => stuff.Name.startsWith(filter)); }
This code illustrates a fixed dependency on the concrete database layer class. This prevents creation of a unit test without a database.
Using an interface we can remove the dependency:
public IEnumerable<Stuff> GetFilteredStuff(string filter, IDatabaseRepository repository) { if (repository.AllowsFiltering()) { var stuff = repository.GetStuffFromDb(); return stuff.Where(s => s.Name.StartsWith(filter)); } return null; }
Now the dependency is gone and we can unit test the method by providing a stub class which implements the IDatabaseRepository interface.
[TestMethod] public void GetFilteredStuff_GivenValidFilter_ReturnsCorrectFilteredResult { IDatabaseRepsitory db = new StubDatabaseRepository(); var result = db.GetFilteredStuff("Bob", db); // Assert expected result }
Unit Testing
So, working this way I can test each individual unit of logic. This promotes a flexible architecture and single responsibility classes and methods. If we were to test the original method with the database dependency by supplying a known database this would be integration testing.
Tools
As TDD has become more popular a network of support tools has flourished which really enable the viability of TDD. These are the main categories:
- Inversion of Control Containers (e.g. Unity - http://unity.codeplex.com/Wikipage)
- Mocking libraries (e.g. Rhino Mocks - http://www.ayende.com/projects/rhino-mocks.aspx)
- Refactoring tools (e.g. Resharper - http://www.jetbrains.com/resharper/) </ul>
Let’s look into each category in turn….
Inversion of Control Containers for Dependency Injection
To remove dependencies we can eliminate 'new myConcreteClass' by passing around interfaces but something has to be responsible for generating the concrete classes. In order to centralise the creation code we can make use of the factory pattern. Luckily we don't need to write the code for the factory in this scenario since there are numerous implementations freely available. These are known as IOC (Inversion of control) containers. e.g. Unity, StructureMap, etc.
Mocking Libraries
Note. The terminology related to using fake implementations can be confusing: I will refer to a stub as a fake which I can configure but I will not assert against its internal state. A mock is more advanced and can track when it’s methods are called, how many times they are called, with which parameters, etc.. These values can be asserted against in your unit tests.
In my unit tests I can use stubbed implementations for the dependent interfaces. Sometimes I would want to generate concrete stubs. However, I can use a Mocking library to automatically generate a stub. I can configure the generated stub to return what I want it to return.
Most mocking libraries work by automatically re-implementing your interface via reflection and so are limited to stubbingmocking interfaces. Some libraries, such as Typemock Isolator and MS Moles use the .NET profiler APIs http://msdn.microsoft.com/en-us/magazine/cc301725.aspx or IL rewriting and can stub/mock any .NET class.
Here’s an example of using a stub to test the previous example (using R|hino Mocks):
[TestMethod] public void GetFilteredStuff_GivenValidFilter_ReturnsCorrectFilteredResult() { var mocks = new MockRepository(); var repo = mocks.Stub<IDatabaseRepository>(); using (mocks.Record()) { repo.Stub(t => t.AllowsFiltering()).Return(true); repo.Stub(t => t.GetStuffFromDb()).Return(new List<Stuff> { new Stuff { Name = "Bob" }, new Stuff { Name = "Fred" } }); } using (mocks.Playback()) { var mc = new MyConcreteClass(); var result = mc.GetFilteredStuff("Bob", repo); Assert.AreEqual(1, result); } }
Notice that the mocking library allows me to dynamically generate the stub class which implements the IDatabaseRepository interface.
Refactoring Tools
Resharper is a refactoring and code productivity tool which integrates with visual studio and provides the following features:
- Refactoring
- Code Analysis - on the fly (squiggly lines)
- Unit testing tools
- Code generation tools
- Code formatting
- Shared coding standards </ul>
Here’s an example of it’s usage Windows Media demo
I personally use resharper and have found it to give a great productivity boost and generally supports my TDD coding amongst other things. (Please email me for a 10% discsount code on resharper licenses – babaandthepigman@hotmail.co.uk ).
Calculator Kata
To practise some TDD skills this gives an interesting exercise http://osherove.com/tdd-kata-1/ for a very simple string calculator (and some videos of people carrying out the exercise).
That’s it for the intro….
Technorati Tags: Introduction, test-driven,development,overview,Basic,Principles,supports,requirements,unit,separation,injection,composition,components,IEnumerable,GetFilteredStuff,DatabaseLayer,GetStuffFromDB,Where,Name,code,dependency,database,layer,creation,interface,IDatabaseRepository,repository,StartsWith,method,implements,TestMethod,GetFilteredStuff_GivenValidFilter_ReturnsCorrectFilteredResult,IDatabaseRepsitory,StubDatabaseRepository,result,Assert,logic,architecture,classes,integration,Tools,Inversion,Control,Containers,Wikipage,Rhino,Resharper,category,factory,scenario,StructureMap,Libraries,Note,terminology,times,Sometimes,library,Most,reflection,Some,Typemock,Isolator,APIs,magazine,Here,example,MockRepository,Stub,Record,Return,List,Playback,MyConcreteClass,AreEqual,Notice,tool,studio,features,Analysis,generation,usage,demo,Calculator,Kata,technologies,concepts,examples,articles,dependencies,methods,categories,interfaces,parameters,Moles,skills,architectures,aspx,implementations,repoWindows Live Tags: Introduction,test-driven,development,overview,Basic,Principles,supports,requirements,unit,separation,injection,composition,components,IEnumerable,GetFilteredStuff,DatabaseLayer,GetStuffFromDB,Where,Name,code,dependency,database,layer,creation,interface,IDatabaseRepository,repository,StartsWith,method,implements,TestMethod,GetFilteredStuff_GivenValidFilter_ReturnsCorrectFilteredResult,IDatabaseRepsitory,StubDatabaseRepository,result,Assert,logic,architecture,classes,integration,Tools,Inversion,Control,Containers,Wikipage,Rhino,Resharper,category,factory,scenario,StructureMap,Libraries,Note,terminology,times,Sometimes,library,Most,reflection,Some,Typemock,Isolator,APIs,magazine,Here,example,MockRepository,Stub,Record,Return,List,Playback,MyConcreteClass,AreEqual,Notice,tool,studio,features,Analysis,generation,usage,demo,Calculator,Kata,technologies,concepts,examples,articles,dependencies,methods,categories,interfaces,parameters,Moles,skills,architectures,aspx,implementations,repo </p></p></div>
Comments