Date post: | 10-May-2015 |
Category: |
Technology |
Upload: | dhaval-dalal |
View: | 3,086 times |
Download: | 0 times |
Test Driven Development
Dhaval Dalalsoftware-artisan.com
What TDD is not?TDD
is not about Testing!
TDDis not
Test Lastor
DDT (Design Driven Tests)
What TDD is?TDDor
Test First is about
evolving the design of the system through Tests.
2
4
3 2
4
5
TDD Episode
[TestFixture]
public class OddNumberFilterTest {
[Test]
public void FiltersOutOddNumbers() {
OddNumberFilter filter = new OddNumberFilter();
int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1));
Assert.That(evenNumbers, Has.Member(4));
Assert.That(evenNumbers, Has.No.Member(3));
}
Specify What Software Should Do...
Then
When
Given
public class OddNumberFilter {
public int [] Filter (params int [] numbers)
{
throw new NotImplementedException();
}
}
Write Just Enough Code To Compile
public class OddNumberFilter {
public int [] Filter (params int [] numbers)
{
List<int> evenNumbers = new List<int>();
foreach (int number in numbers)
{
if (number % 2 == 0)
{
evenNumbers.Add(number);
}
}
return evenNumbers.ToArray();
}
Write Enough Code To Pass The Specification...
General Test Structure
Setup the Givens(Context in which the Test runs)
Then verify the Assertions(State or Behavior verifications)
Exercise the Whens(Perform the actual operation)
Arrange
Assert
Act
[TestFixture]
public class NumberFilterTest {
[Test] public void FiltersOddNumbers() {
OddNumberFilter filter = new OddNumberFilter(); int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1)); Assert.That(evenNumbers, Has.Member(4)); Assert.That(evenNumbers, Has.No.Member(3));
}
Add New Feature: Filter-out Non-Primes
[TestFixture]
public class NumberFilterTest {
[Test] public void FiltersOddNumbers() {
OddNumberFilter filter = new OddNumberFilter(); int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1)); Assert.That(evenNumbers, Has.Member(4)); Assert.That(evenNumbers, Has.No.Member(3));
}
[Test]
public void FiltersNonPrimes() {
NonPrimesFilter filter = new NonPrimesFilter();
int [] numbers = new int [] { 4, 5 };
int [] primeNumbers = filter.Filter(numbers);
Assert.That(primeNumbers, Has.Count(1));
Assert.That(primeNumbers, Has.Member(5));
Assert.That(primeNumbers, Has.No.Member(4));
}
Add New Feature: Filter-out Non-Primes
[TestFixture]
public class NumberFilterTest {
[Test] public void FiltersOddNumbers() {
OddNumberFilter filter = new OddNumberFilter(); int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1)); Assert.That(evenNumbers, Has.Member(4)); Assert.That(evenNumbers, Has.No.Member(3));
}
[Test]
public void FiltersNonPrimes() {
NonPrimesFilter filter = new NonPrimesFilter();
int [] numbers = new int [] { 4, 5 };
int [] primeNumbers = filter.Filter(numbers);
Assert.That(primeNumbers, Has.Count(1));
Assert.That(primeNumbers, Has.Member(5));
Assert.That(primeNumbers, Has.No.Member(4));
}
Add New Feature: Filter-out Non-Primes
Duplicationof Concept
[TestFixture]
public class NumberFilterTest {
[Test] public void FiltersOddNumbers() {
OddNumberFilter filter = new OddNumberFilter(); int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1)); Assert.That(evenNumbers, Has.Member(4)); Assert.That(evenNumbers, Has.No.Member(3));
}
[Test]
public void FiltersNonPrimes() {
NonPrimesFilter filter = new NonPrimesFilter();
int [] numbers = new int [] { 4, 5 };
int [] primeNumbers = filter.Filter(numbers);
Assert.That(primeNumbers, Has.Count(1));
Assert.That(primeNumbers, Has.Member(5));
Assert.That(primeNumbers, Has.No.Member(4));
}
Add New Feature: Filter-out Non-Primes
Duplicationof Concept
Duplicationof Concept
public interface IFilter {
int [] filter.Filter(param int [] numbers);
}
Introduce Abstraction
[TestFixture]
public class NumberFilterTest {
[Test] public void FiltersOutOddNumbers() { IFilter filter = new OddNumberFilter(); int [] numbers = new int [] { 3, 4 };
int [] evenNumbers = filter.Filter(numbers);
Assert.That(evenNumbers, Has.Count(1)); Assert.That(evenNumbers, Has.Member(4)); Assert.That(evenNumbers, Has.No.Member(3));
}
[Test] public void FiltersOutNonPrimes() { IFilter filter = new NonPrimesFilter(); int [] numbers = new int [] { 4, 5 };
int [] primeNumbers = filter.Filter(numbers);
Assert.That(primeNumbers, Has.Count(1)); Assert.That(primeNumbers, Has.Member(5)); Assert.That(primeNumbers, Has.No.Member(4));
}
Refactored Code
Red Green
TDD Rhythm
Refactor
TDD Rhythm Flowchart
Write just enough Code to compile
Refactor CodePass the Test
(GREEN)
Fail the Test(RED)
Write just enough Code to pass the test
Specify whatthe software should do
Pass the Test(GREEN)
So, TDD is about…
analyzing what little you actually need to do and how cleanly you can do it!
Carving design of your code a unit test at a time.
Write Tests Before Writing Code
Focuses the mind (and the development process) Deliver only what is absolutely necessary.
System so developed does exactly what it needs to do and no more.
Need not code for future YAGNI (You Ain’t Gonna Need It!)...no gold plating!
TDD is a Design Technique Makes you think in terms of Object
behavior. How client is going to interact with the object.
Outside-In Object so created is “consumer aware”
What Object provides and needs from environment.
Traditional OOD focuses only on Object’s Implementation
Inside-Out
TDD results in a Decoupled Design Favors Composition over Inheritance.
Relies on dependency injection for collaborators.
Avoids tight coupling to global objects Singletons mix static and state. Makes design untestable.
Many small, loosely coupled classes.
Makes you think of inter-object interactions in terms of interfaces. Promotes Programming to Super-Types and not
Concretes.
Benefits of TDD Test-a-little and build-a-little gives
confidence Green bar gives you confidence Reduces fear of change
Documentation Provides starting point to understand code
functionality Safety Net
Checks Regression Supports Refactoring
Benefits of TDD Effort
Reduces effort to final delivery Writing tests is more productive
Predictable Tells me when am I done Continuous Success Vs Illusive Success
Immediate Feedback Makes failures shine at you
Costs of TDDClaim: It is too much work to write tests!Rebut: I’d say “are you looking to create a speculative
design?”, “do you want to sleep with a debugger?”
Claim: Tests themselves are code, we need to maintain them, that’s overhead!
Rebut: I’d say “do you prefer maintaining a big bug list?”
Claim: I have to spend time re-orient my thinking!Rebut: I’d say “Just as with any skill, you need to allow some
time to apply this effectively.”
Writing Good Tests
Better Test Practices Tests must be Small.
Easy to understand They do not break when other parts of the code are
changed. One behavioral-assert per test.
Tests must be Expressive. Test code should communicate its intent. It should not take more than 2 minutes for readers to
understand what is going on.
Tests must be Maintainable. When a test breaks, what it contains should be easiest
to fix.
Better Test Practices Tests must execute Fast.
Slow running tests increase Build Viscosity.
Tests are Specifications, not Verifications. Do not verify whether the code does what its supposed
to do correctly. Specify what should the code do to function correctly.
Tests should talk the Domain Language. Communicate the behavior under test and not how the
system implements that behavior. Improves Communication with Non-Technical Members
on the team. Developers can understand domain faster.
Better Test Practices Tests must run at will.
Able to write and execute tests without worrying about how to execute them.
Tests must be Isolated Very little set-up and minimum collaborators.
Tests must be Thorough Test all behavior, not methods.
Better Test Practices Tests must be Automated.
Write them such that methods on objects are invoked by code rather than by hand.
Tests must be Self-Verifiable. Setup test expectations and compare outcome with
expectations for verdicts.
Tests must be Repeatable. Executing the same test several times under the same
conditions must yield same results.
JUnit/NUnit Better Test Practices Test anything that could possibly break. Make testing exceptional scenarios easy to read. Always explain failure reason in Assert calls. Test should usually improve the design of the
code. For JUnit Tests
Make Test code reside in same packages, but different directories.
For NUnit Tests Make Test code reside in separate project from
source project, in same namespace.
Flavors of Tests Object Tests (Unit or Programmer Tests)
Tests behavior of single object at a time.
Integration Tests Tests collaboration of a number of objects (how they talk
to each other) Complex fixtures More brittle
End-to-End Tests Thoroughly test the entire system from end-to-end.
Unit Testing Frameworks xUnit framework for Unit Testing
JUnit/TestNG for Java NUnit/MbUnit for C# cppUnit for C++ pyUnit for Python …and tons of more Unit Testing frameworks.
References JUnit Recipes
J. B. Rainsberger
JUnit In Action Vincent Massol
Agile Java Jeff Langr
Test-Driven Development Rhythm Gunjan Doshi
Agile Principles, Patterns, and Practices in C# Robert C. Martin, Micah Martin
xUnit Test Patterns Gerard Meszaros
On TDD (InfoQ): How Do We Know When We’re Done?
Steve Freeman
10-Ways-to-Better-Code (InfoQ) Neal Ford
Refactoring Away Duplicated Logic Creates a Domain Specific Embedded Language for Testing
Nat Pryce
Jay Field’s Blog Entry http://blog.jayfields.com/
2007/06/testing-inline-setup.html