About Test Driven Development
I-Overview
===========
1.What is Test Driven Development(TDD)
Test-driven Development (TDD) is the craft of producing automated tests for production
code, and using that process to drive design and programming. For every tiny bit of
functionality in the production code, you first develop a test that specifies and
validates what the code will do. You then produce exactly as much code as will enable
that test to pass. Then you refactor (simplify and clarify) both the production code
and the test code.--From www.agilealliance.org
2.Several kind of tests.
(1).Unit Test. A tool of developers.Small scope,one class usually.
(2).Acceptance Test. Also called as Functional Test.Perform by customer.
Which one we should concentrate on?Both,They affect in different scene.
3.What is the benefit
A list of ways that test-first programming can affect design:
(1).Re-use is good. Test first code is born with two clients, not one. This makes
adding a third client twice as easy.
(2).Refactoring test-first code results in equally tested code, permitting more
aggressive refactorings. Cruft is not allowed, and code is generally in better
shape to accept more refactorings.
(3).When paying attention during all of the little steps, you may discover
patterns in your code.
(4).Test code is easy to write. It's usually a couple calls to the server object,
then a list of assertions. Writing the easy code first makes writing the hard code easy.
(5).DesignPatterns may be incremented in, not added all of a bunch up front.
(6).Test-first code is written Interface first. You think of the simplest
interface that can show function.
(7).Code tends to be less coupled. Effective unit tests only test one thing. To do
this you have to move the irrelevant portions out of the way (e.g., MockObjects).
This forces out what might be a poor design choice.
(8).UnitTests stand as canonical & tested documentation for objects' usage. Developers
read them, and do what they do in production code to the same objects. This keeps
projects annealed and on track.
(9).When the developer has to write tests for what he is going to do, he is far less
likely to add extraneous capabilities. This really puts a damper on developer
driven scope creep.
(10).Test First Design forces you to really think about what you are going to do.
It gets you away from "It seemed like a good idea at the time" programming.
4.Rhythm of TDD
(1).Write a unit test that the system currently doesn’t pass.
(2).Quickly write the code to pass that test.
(3).Refactor the code to remove any duplication or other design nasties
(4).Move on to the next unit test
5.Detailed Step
(1).Think about what you want to do. (**Critical**)
(2).Think about how to test it. (**Critical**)
(3).Write a small test. Think about the desired API.
(4).Write just enough code to fail the test.
(5).Run and watch the test fail. (The test-runner, if you're using something like
JUnit, shows the "Red Bar"). Now you know that your test is going to be executed.
(6).Write just enough code to pass the test (and pass all your previous tests).
(7).Run and watch all of the tests pass. (The test-runner, if you're using JUnit, etc.,
shows the "Green Bar"). If it doesn't pass, you did something wrong, fix it now
since it's got to be something you just wrote.
(8).If you have any duplicate logic, or inexpressive code, refactor to remove
duplication and increase expressiveness -- this includes reducing coupling and
increasing cohesion.
(9).Run the tests again, you should still have the Green Bar. If you get the Red Bar,
then you made a mistake in your refactoring. Fix it now and re-run.
(10).Repeat the steps above until you can't find any more tests that drive writing new code.
II-Getting Started
==================
1.Preparation
(1).Choose right Unittest Framework(UF).The UF is some develop tools with which one can
easily create tests,orginaze them and view the fedback.
(2).Setting up UF.Using UF to perform test.
(3).CppUnit is a C++ unit testing framework. It started its life as a port of JUnit to
C++ by Michael Feathers.
2.Action(Example using CPPUnit--an UF for cpp)
(1).Write a unit test that the system currently doesn’t pass.
For example, to test the equality comparison for a MyComplex number class, write:
class ComplexNumberTest : public CppUnit::TestCase {
public:
ComplexNumberTest( std::string name ) : CppUnit::TestCase( name ) {}
void runTest() {
CPPUNIT_ASSERT( MyComplex (10, 1) == MyComplex (10, 1) );
CPPUNIT_ASSERT( !(MyComplex (1, 1) == MyComplex (2, 2)) );
}
};
(2).Quickly write the code to pass that test.
In order to pass the test quickly, we write a class named MyComplex.
class MyComplex {};
//If we compile now ,we get compile error.So keep fixing it.
bool operator==( const MyComplex &a, const MyComplex &b)
{
return true;
}
//Now compile it again,Ok!Run it,we'll get some fail.
//This is because the operator==() doesn't work properly.Keep fixing it.
class MyComplex {
friend bool operator ==(const MyComplex& a, const MyComplex& b);
double real, imaginary;
public:
MyComplex( double r, double i = 0 )
: real(r)
, imaginary(i)
{
}
};
bool operator ==( const MyComplex &a, const MyComplex &b )
{
return a.real == b.real && a.imaginary == b.imaginary;
}
//If we compile now and run our test it will pass.
(3).Refactor the code to remove any duplication or other design nasties
//Here I found the name "MyComplex" is not good,I Change them to "Complex"
//using replace.
(4).Move on to the next unit test
//Now it worked ok.Move to other cases.Such as GetMod()...
(5).Use containers to hold cases.
There'll always be more than one test case.So we need to organize them
into some friendly format.Here we have CppUnit::TestFixture to hold a
lot of test cases.
//Code begin...
class ComplexNumberTest : public CppUnit::TestFixture {
private:
Complex *m_10_1, *m_1_1, *m_11_2;
public:
void setUp()
{
m_10_1 = new Complex( 10, 1 );
m_1_1 = new Complex( 1, 1 );
m_11_2 = new Complex( 11, 2 );
}
void tearDown()
{
delete m_10_1;
delete m_1_1;
delete m_11_2;
}
void testEquality()
{
CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
}
void testAddition()
{
CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
}
};
//Code end...
Also there is often more than one test fixture,so we use CppUnit::TestSuite
to orginaze more than one "Fixture".
//Code begin...
CppUnit::TestSuite suite;
CppUnit::TestResult result;
suite.addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testEquality",
&ComplexNumberTest::testEquality ) );
suite.addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testAddition",
&ComplexNumberTest::testAddition ) );
suite.run( &result );
//Code end...
3.Deeper thinking
(1).What can we do when the background is complicated?
When we implement some components which relate to database or GUI,what can we do
to test it? Mock Objects. What is mock objects? For example, when use a
database for querying some data, what can we do if the database server is not
ready yet? We can give a simple class which implement the functions just simple
and quick, then we can test our function locally.Below is the Definition about
Mock Object--from www.mockobjects.com
A mock object is a "double agent" used to test the behaviour of other objects.
First, a mock object acts as a faux implementation of an interface or class that
mimics the external behaviour of a true implementation. Second, a mock object
observes how other objects interact with its methods and compares actual
behaviour with preset expectations. When a discrepancy occurs, a mock object
can interrupt the test and report the anomaly. If the discrepancy cannot be noted
during the test, a verification method called by the tester ensures that all
expectations have been met or failures reported.
What situation we should consider about using mock objects?
* The real object has nondeterministic behavior (it produces
unpredictable results; as in a stock-market quote
feed.)
* The real object is difcult to set up.
* The real object has behavior that is hard to trigger (for
example, a network error).
* The real object is slow.
* The real object has (or is) a user interface.
* The test needs to ask the real object about how it was
used (for example, a test might need to check to see that
a callback function was actually called).
* The real object does not yet exist (a common problem
when interfacing with other teams or new hardware systems).
The three key steps to using mock objects for testing are:
1. Use an interface to describe the object
2. Implement the interface for production code
3. Implement the interface in a mock object for testing
III-Resources
=============
Wiki About TDD
http://c2.com/cgi/wiki?TestDrivenDevelopment
Unittests in extremeprogramming.com
http://www.extremeprogramming.org/rules/unittests.html
Unittest Patterns
http://www.codeproject.com/script/admentor/include/ServeAdHTML.aspx?C=False&id=786
本文地址:http://com.8s8s.com/it/it33758.htm