Many practices purport to support such a quick-on-your-feet motif, of which Extreme Programming is only one.[20] In this section we explore what we think is the key to making flexible, incremental development succeed: a ridiculously easy-to-use automated unit test framework. (Please note that we in no way mean to de-emphasize the role of
Developers write
1.I understand the requirements.
2.My code meets those requirements to the best of my knowledge.
There is no better way to ensure that you know what the code you're about to write should do than to write the unit tests first. This simple exercise helps focus the mind on the task ahead and will likely lead to working code faster than just jumping into coding. Or, to express it in XP terms: Testing + Programming is
Once your code passes all your tests, you have the peace of mind that if the system you contribute to isn't working, it's not your fault. The statement "All my tests pass" is a powerful trump card in the workplace that cuts through any amount of politics and hand waving.
Automated testing
So what does a unit test look like? Too often developers just use some well-behaved input to produce some expected output, which they inspect visually. Two dangers exist in this approach. First, programs don't always receive only well-behaved input. We all know that we should test the boundaries of program input, but it's hard to think about this when you're trying to just get things working. If you write the test for a function first before you start coding, you can wear your "tester hat" and ask yourself, "What could possibly make this break?" Code a test that will prove the function you'll write isn't broken, and then put on your developer hat and make it happen. You'll write better code than if you hadn't written the test first.
The second danger is that inspecting output visually is tedious and error prone. Most any such thing a human can do a computer can do, but without human error. It's better to formulate tests as collections of
For example, suppose you need to build a Date class that has the following properties:
· A date can be initialized with a string (YYYYMMDD), three integers (Y, M, D), or nothing (giving today's date).
· A date object can yield its year, month, and day or a string of the form "YYYYMMDD".
· All relational comparisons are available, as well as computing the duration between two dates (in years, months, and days).
· Dates to be compared need to be able to span an arbitrary number of centuries (for example, 1600–2200).
Your class can store three integers representing the year, month, and day. (Just be sure the year is at least 16 bits in size to satisfy the last bulleted item.) The interface for your Date class might look like this:.
// A first pass at Date.h
#ifndef DATE_H
#define DATE_H
#include
class Date {
public:
// A struct to hold elapsed time:
struct Duration {
int years;
int months;
int days;
Duration(int y, int m, int d)
: years(y), months(m), days(d) {}
};
Date();
Date(int year, int month, int day);
Date(const std::string&);
int getYear() const;
int getMonth() const;
int getDay() const;
std::string toString() const;
friend bool operator<(const Date&, const Date&);
friend bool operator>(const Date&, const Date&);
friend bool operator<=(const Date&, const Date&);
friend bool operator>=(const Date&, const Date&);
friend bool operator==(const Date&, const Date&);
friend bool operator!=(const Date&, const Date&);
friend Duration duration(const Date&, const Date&);
};
#endif
Before you even think about implementation, you can solidify your grasp of the requirements for this class by writing the beginnings of a test program. You might come up with something like the following:
//: C02:SimpleDateTest.cpp
//{L} Date
// You’ll need the full Date.h from the Appendix:
#include "Date.h"
#include
using namespace std;
// Test machinery
int nPass = 0, nFail = 0;
void test(bool t) {
if(t) nPass++; else nFail++;
}
int main() {
Date mybday(1951, 10, 1);
test(mybday.getYear() == 1951);
test(mybday.getMonth() == 10);
test(mybday.getDay() == 1);
cout << "Passed: " << nPass << ", Failed: "
<< nFail << endl;
}
/* Expected output:
Passed: 3, Failed: 0
*/ ///:~