3.03 路由树-TDD起步

在有了类型定义之后,我们就可以考虑按照TDD的思路,用测试来驱动我们的实现.

我们使用简化版的TDD,即:

  1. 定义API(这一步上节课已经定义好了)

  2. 定义测试

  3. 添加测试用例

  4. 实现,并且确保实现能够通过测试用例

  5. 重复3-4直到考虑了所有的场景

  6. 重复步骤1-5

PART1. 定义测试文件

初态工程结构如下:

(base) yanglei@yuanhong 07-TDD % tree ./
./
├── context.go
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── node.go
├── router.go
└── serverInterface.go

0 directories, 7 files

我们本节课主要是针对路由树进行测试,实际上是要测试router.AddRoute()方法注册路由的结果是否符合预期.因此创建文件router_test.go

1.1 构造与验证路由树

我们要测试的是注册路由后,路由树的结构和预期是否相同,因此测试分为2个步骤:

  1. 构造路由树

  2. 验证路由树

router_test.go:

此处我们其实并不关心HandleFunc,因此也就没有在TestNode结构体上定义这个字段,调用router.AddRoute()方法时也是传入了一个mock值

1.2 断言路由树

注意断言的时候我们无法使用assert.Equal(),因为HandleFunc类型不是可比较的.也就是说以下代码是不行的:

因此我们需要自定义判断二者是否相等的方法.

1.2.1 断言router是否相等

此处我们需要判断2个router是否相等.逻辑上很简单:

  • 如果两个路由森林中的路由树数量不同,则不相等

  • 如果目标router中没有对应HTTP方法的路由树,则不相等

  • 比对相同HTTP方法的路由树结构是否相等.这个方法下一小节实现

1.2.2 断言node是否相等

虽然上述代码的注释中写的是"比对两棵路由树的结构是否相等",但实际上它们都是node结构体的实例,因此在node结构体上实现equal()方法即可.

断言node是否相等的逻辑就稍微复杂一些:

  • 如果目标节点为nil,则不相等

  • 如果两个节点的path不相等,则不相等

  • 若两个节点的子节点数量不相等,则不相等

  • 若两个节点的handleFunc类型不同,则不相等(TODO:反射我用的很少,这块代码是直接抄的)

  • 比对两个节点的子节点映射是否相等

    • 如果源节点的子节点中,存在目标节点没有的子节点,则不相等

    • 两个path相同的节点再次递归比对

PART2. 添加测试用例

假定我们注册了一个名为/user/home的路由,那么我们预期的路由树结构应该如下图示:

预期路由树结构

需要注意的是,只有"home"节点是有HandleFunc的.因为最终路由是注册在"home"节点上的.

PART3. 实现AddRoute方法

3.1 查找或创建子节点

3.1.1 根据method查找根节点

这一步比较简单,根据根据method查找根节点,不存在则创建

router.go:

3.1.2 在根节点上查找目标节点

这里就需要考虑:

  • 若在根节点的children映射中查找到了目标节点,则添加HandleFunc后返回

  • 若在根节点的children映射中没有查找到子节点,则创建目标节点

    • 树中途存在未被创建的节点,则创建该节点,然后以该节点为目标节点,继续创建子节点,直到找到目标节点为止

    • 为目标节点添加HandleFunc

node.go:

到这一步,就可以跑一下单测了

附录

发现自身的问题

  1. 这里老师debug的过程是比我日常开发的方式要强很多的,我还是最古老的fmt.Printf()的方式

    • 学人家怎么用IDE去Debug的

  2. 但更深层次的原因是,他写了单测,才能支持他把代码运行起来.我以后也得考虑用这种方式开发,会高效很多

Last updated