1. 概述

本教程将带你使用测试驱动开发(TDD)流程,一步步实现一个自定义的List

⚠️ 这不是TDD入门教程,我们假设你已经了解TDD的基本概念,并希望提升相关实践能力。

简单说,TDD是一种设计工具,能帮助我们通过测试驱动实现

提前说明:我们重点展示TDD实践而非高效实现,所以性能优化不是本文目标。

2. 准备工作

先定义类的基本骨架:

public class CustomList<E> implements List<E> {
    private Object[] internal = {};
    // 空实现的方法
}

CustomList实现了List接口,因此必须包含该接口声明的所有方法。为通过编译,我们先提供空方法体:有返回值的方法可返回任意默认值(如Object返回nullboolean返回false)。

为简洁起见,我们省略了可选方法和部分不常用的必选方法。

3. TDD循环流程

使用TDD开发意味着:

  1. 先写测试 → 用测试定义需求
  2. 再实现功能 → 让测试通过(代码优雅性暂不考虑)
  3. 最后重构 → 提升代码可读性和可维护性(保持测试通过)

我们将通过几个List接口方法演示这个循环,从最简单的开始。

4. isEmpty方法

isEmpty可能是List接口最简单的方法。以下是初始实现:

@Override
public boolean isEmpty() {
    return false;
}

这个初始实现足够编译,后续测试会"逼"我们改进它。

4.1 第一个循环

写测试验证空列表应返回true

@Test
public void givenEmptyList_whenIsEmpty_thenTrueIsReturned() {
    List<Object> list = new CustomList<>();
    assertTrue(list.isEmpty());
}

测试失败(方法总是返回false)。简单粗暴地修复:

@Override
public boolean isEmpty() {
    return true;
}

4.2 第二个循环

添加测试验证非空列表应返回false

@Test
public void givenNonEmptyList_whenIsEmpty_thenFalseIsReturned() {
    List<Object> list = new CustomList<>();
    list.add(null);

    assertFalse(list.isEmpty());
}

现在需要实现add方法:

@Override
public boolean add(E element) {
    return false;
}

这个实现无效(未修改内部结构)。改进它:

@Override
public boolean add(E element) {
    internal = new Object[] { element };
    return false;
}

测试仍然失败(isEmpty未改进)。继续修复:

@Override
public boolean isEmpty() {
    if (internal.length != 0) {
        return false;
    } else {
        return true;
    }
}

✅ 非空测试通过。

4.3 重构

当前代码不够优雅,优化一下:

@Override
public boolean isEmpty() {
    return internal.length == 0;
}

✅ 测试仍然通过,isEmpty方法完成。

5. size方法

初始实现仅用于编译:

@Override
public int size() {
    return 0;
}

5.1 第一个循环

利用已有的add方法测试单元素列表大小:

@Test
public void givenListWithAnElement_whenSize_thenOneIsReturned() {
    List<Object> list = new CustomList<>();
    list.add(null);

    assertEquals(1, list.size());
}

测试失败(返回0)。修复实现:

@Override
public int size() {
    if (isEmpty()) {
        return 0;
    } else {
        return internal.length;
    }
}

5.2 重构

简化实现:

@Override
public int size() {
    return internal.length;
}

✅ 方法实现完成。

6. get方法

初始实现:

@Override
public E get(int index) {
    return null;
}

6.1 第一个循环

测试单元素列表获取:

@Test
public void givenListWithAnElement_whenGet_thenThatElementIsReturned() {
    List<Object> list = new CustomList<>();
    list.add("baeldung");
    Object element = list.get(0);

    assertEquals("baeldung", element);
}

简单实现通过测试:

@Override
public E get(int index) {
    return (E) internal[0];
}

6.2 改进

通常应先写更多测试再改进,但需要其他方法配合(这些方法尚未成熟)。这里我们打破TDD循环,直接完善实现——其实很简单:

@Override
public E get(int index) {
    return (E) internal[index];
}

⚠️ 注意:实际项目中应严格遵循TDD循环。

7. add方法

当前实现(来自第4节):

@Override
public boolean add(E element) {
    internal = new Object[] { element };
    return false;
}

7.1 第一个循环

测试返回值:

@Test
public void givenEmptyList_whenElementIsAdded_thenGetReturnsThatElement() {
    List<Object> list = new CustomList<>();
    boolean succeeded = list.add(null);

    assertTrue(succeeded);
}

修改返回值:

@Override
public boolean add(E element) {
    internal = new Object[] { element };
    return true;
}

✅ 测试通过,但存在明显问题:添加第二个元素会覆盖第一个。

7.2 第二个循环

添加测试验证多元素支持:

@Test
public void givenListWithAnElement_whenAnotherIsAdded_thenGetReturnsBoth() {
    List<Object> list = new CustomList<>();
    list.add("baeldung");
    list.add(".com");
    Object element1 = list.get(0);
    Object element2 = list.get(1);

    assertEquals("baeldung", element1);
    assertEquals(".com", element2);
}

测试失败(当前实现无法添加多个元素)。修复实现:

@Override
public boolean add(E element) {
    Object[] temp = Arrays.copyOf(internal, internal.length + 1);
    temp[internal.length] = element;
    internal = temp;
    return true;
}

✅ 实现已足够优雅,无需重构。

8. 总结

本文演示了通过TDD流程实现自定义List的过程。使用TDD可以: ✅ 逐步实现需求
✅ 保持高测试覆盖率
✅ 确保代码可测试性(因为代码是为通过测试而写)

⚠️ 本文实现的类仅作演示,实际项目中请勿直接使用。

完整源码(包括省略的测试和实现方法)见GitHub仓库


原始标题:How to TDD a List Implementation in Java | Baeldung

» 下一篇: JSON-Java 库详解