循序渐进教程 · 测试驱动开发

用 TDD 编写 NES 模拟器

通过 356 节测试驱动的课程,用 Python 从零开始构建一台任天堂红白机(NES)模拟器。

作者: linkfy · 14 · 356 节课

本教程是什么

这是一个只包含测试的起始项目。它有意不包含完成后的模拟器:tests/ 目录就是课程本身。每个编号测试都会说明下一个行为为什么重要、需要创建或更新哪个生产代码文件,以及在该阶段所期望的最小实现。

本教程走的是稳扎稳打的路线,而不是速成路线。

前几章构建并验证 CPU、内存、卡带和 PPU 的基础。游戏画面与交互式前端会在后面出现,等支撑它们的组件通过测试确立之后再登场。不要期待前几节课就能看到画面;请期待当画面最终出现时,你能理解它为什么能运行。

快速开始

  1. 1. 克隆仓库

    git clone https://github.com/linkfy/nes-emulator-tutorial-tdd
    cd nes-emulator-tutorial-tdd
  2. 2. 安装 uv 并创建锁定环境

    不要在仓库内运行 uv init:项目已经初始化过了。在项目根目录安装 uv.lock 中固定的依赖。

    uv --version
    uv sync

    docs.astral.sh/uv

  3. 3. 打开第 001 课并只运行该测试

    在编写生产代码之前,先从头到尾阅读该模块级文档。然后只运行这一节课:

    uv run pytest tests/chapter_01_cpu/test_001_initial_files.py -v
  4. 4. 重复 红 → 绿 → 重构

    一次只推进一个编号测试。在继续之前,每节已完成的课程都必须保持绿色。只运行已完成的测试;先不要运行后面的章节。

    完成第 356 课之后,运行完整测试套件:

    uv run pytest

TDD 循环

运行本课的测试并看着它失败。

绿

写出能让它通过的最小实现。

重构

在保持之前所有测试为绿色的前提下整理代码。

代码中的标记

课程示例可能使用 NEW LINE / NEW BLOCK 表示要新增的代码,UPDATED LINE / UPDATED BLOCK 表示要修改的代码,DELETED LINE / DELETED BLOCK 表示废弃的代码,以及 ... 表示现有代码保持不变。

课程大纲

十四章,356 节按顺序编排的课程。用这些编号范围了解你的进度和接下来的内容。

手动测试用 ROM

部分后期课程包含使用合法获取的真实 ROM(例如《超级马力欧兄弟》或《马力欧兄弟》)进行的手动检查。只有当某节课到达手动检查点时才需要它们。