# 测试驱动设计，第 1 部分

## TDD 工作流程

##### 图 1. TDD 工作流程

1. 编写一个失败的测试。
2. 编写代码以使测试通过。
3. 重复第 1 步和第 2 步。
4. 在此过程中积极地重构。
5. 当您无法再想到任何测试时，那么就必须做决策了。

## 完全数

### 后测试开发

##### 清单 1. 后测试开发的 `PerfectNumberFinder`
```public class PerfectNumberFinder1 {
public static boolean isPerfect(int number) {
// get factors
List<Integer> factors = new ArrayList<Integer>();
for (int i = 2; i < number; i++)
if (number % i == 0)

// sum factors
int sum = 0;
for (int n : factors)
sum += n;

// decide if it's perfect
return sum - number == number;
}
}```

##### 清单 2. `PerfectNumberFinder` 的单元测试
```public class PerfectNumberFinderTest {
private static Integer[] PERFECT_NUMS = {6, 28, 496, 8128, 33550336};

@Test public void test_perfection() {
for (int i : PERFECT_NUMS)
assertTrue(PerfectNumberFinder1.isPerfect(i));
}

@Test public void test_non_perfection() {
List<Integer>expected = new ArrayList<Integer>(
Arrays.asList(PERFECT_NUMS));
for (int i = 2; i < 100000; i++) {
if (expected.contains(i))
assertTrue(PerfectNumberFinder1.isPerfect(i));
else
assertFalse(PerfectNumberFinder1.isPerfect(i));
}
}

@Test public void test_perfection_for_2nd_version() {
for (int i : PERFECT_NUMS)
assertTrue(PerfectNumberFinder2.isPerfect(i));
}

@Test public void test_non_perfection_for_2nd_version() {
List<Integer> expected = new ArrayList<Integer>(Arrays.asList(PERFECT_NUMS));
for (int i = 2; i < 100000; i++) {
if (expected.contains(i))
assertTrue(PerfectNumberFinder2.isPerfect(i));
else
assertFalse(PerfectNumberFinder2.isPerfect(i));
}
assertTrue(PerfectNumberFinder2.isPerfect(PERFECT_NUMS[4]));
}
}```

##### 清单 3. 算法的改进版本
```public class PerfectNumberFinder2 {
public static boolean isPerfect(int number) {
// get factors
List<Integer> factors = new ArrayList<Integer>();
for (int i = 2; i <= sqrt(number); i++)
if (number % i == 0) {
}

// sum factors
int sum = 0;
for (int n : factors)
sum += n;

// decide if it's perfect
return sum - number == number;
}
}```

##### 清单 4. 修正的改进算法
```for (int i = 2; i <= sqrt(number); i++)
if (number % i == 0) {
if (number / i !=  i)
}```

## 通过 TDD 进行紧急设计

• 我需要所求数字的因子。
• 我需要确定某个数字是不是因子。
• 我需要把因子加起来。

##### 清单 5. 测试 “数字是不是因子？”
```public class Classifier1Test {

@Test public void is_1_a_factor_of_10() {
assertTrue(Classifier1.isFactor(1, 10));
}
}```

##### 清单 6. 确定因子的方法
```public class Classifier1 {
public static boolean isFactor(int factor, int number) {
return number % factor == 0;
}
}```

##### 清单 7. 下一个测试：数字的因子
```@Test public void factors_for() {
int[] expected = new int[] {1};
assertThat(Classifier1.factorsFor(1), is(expected));
}```

##### 清单 8. 简单的 `factorsFor()` 方法
```public static int[] factorsFor(int number) {
return new int[] {number};
}```

##### 清单 9. 改进后的 `Classifier` 类
```public class Classifier2 {
private int _number;

public Classifier2(int number) {
_number = number;
}

public boolean isFactor(int factor) {
return _number % factor == 0;
}
}```

##### 清单 10. 下一个测试：数字的因子
```@Test public void factors_for_6() {
int[] expected = new int[] {1, 2, 3, 6};
Classifier2 c = new Classifier2(6);
assertThat(c.getFactors(), is(expected));
}```

##### 清单 11. `getFactors()` 方法的第一步
```public int[] getFactors() {
List<Integer> factors = new ArrayList<Integer>();
for (int i = 2; i < _number; i++) {
if (isFactor(i))
}
int[] intListOfFactors = new int[factors.size()];
int i = 0;
for (Integer f : factors)
intListOfFactors[i++] = f.intValue();
return intListOfFactors;
}```

• 把程序划分为多个可执行一项可识别任务的方法。
• 把方法中的所有操作保持在同一个抽象级别
• 这将自然而然地得到拥有许多小方法的程序，每个小方法都只有几行代码。

1. `factors` 提升为内部状态。
2. `factors` 的初始化代码移到构造函数中。
3. 去掉对 `int[]` 代码的转换，等到它变得有益时再处理它。
4. 添加 `addFactors()` 的另一项测试。

##### 清单 12. 测试 `addFactors()`
```@Test public void add_factors() {
Classifier3 c = new Classifier3(6);
assertThat(c.getFactors(), is(Arrays.asList(1, 2, 3, 6)));
}```

##### 清单 13. 添加因子的简单代码
```public void addFactor(int factor) {
}```

## 结束语

#### 评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=378750
ArticleTitle=演化架构与紧急设计: 测试驱动设计，第 1 部分
publish-date=03272009