3.05 路由树-静态匹配之路由查找

PART1. 定义查找路由的方法

所谓查找路由,就是根据给定的HTTP动词和uri,在路由树中查找对应的节点.

router.go:

// findRoute 根据给定的HTTP方法和路由路径,在路由森林中查找对应的节点
func (r *router) findRoute(method string, path string) (*node, bool) {
	// 沿着树深度遍历
}

注:此处也没有把全部router.go的代码放进来,因为太乱了,只放了和本小节有关的部分

PART2. 定义测试用例

2.1 在测试函数中注册路由

这一部分和注册路由的测试用例代码完全相同

router_test.go:

// TestNode 测试路由树节点
// 由于此处我们要测试的是路由树的结构,因此不需要在测试路由树节点中添加路由处理函数
// 调用addRoute时写死一个HandleFunc即可
type TestNode struct {
	method string
	path   string
}

// TestRouter_findRoute 测试路由查找功能
func TestRouter_findRoute(t *testing.T) {
	// step1. 构造路由树
	testRoutes := []TestNode{}

	r := newRouter()
	mockHandleFunc := func(ctx Context) {}

	for _, testRoute := range testRoutes {
		r.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
	}
}

2.2 构造测试用例

2.2.1 定义测试用例的类型

这里我们需要通过这个类型知道如下信息:

  • 在给定的HTTP动词和uri的前提下,是否在路由树中找到了节点?

  • 在给定的HTTP动词和uri的前提下,找到的节点和预定义的节点是否相同?

router_test.go:

2.2.2 定义测试的过程

  • step1. 判断是否在路由树中找到节点

  • step2. 判断找到的节点和预定义的节点是否相同

    • 这里需要注意的是,和之前写addRoute()方法的测试用例相同,不能用assert.Equal()方法直接比对两个node结构体的实例,因为HandleFunc不可比

router_test.go:

2.2.3 构造测试用例

所谓构造测试用例,就是要考虑findRoute()方法会遇到什么场景(或者也可以说遇到什么边缘条件):

  • HTTP动词不存在

  • 完全命中

  • 命中了但找到的node中,handler是nil

  • 根节点

  • 没有找到path

PART3. 以TDD的方式开发findRoute()方法

3.1 HTTP动词不存在的情况

3.1.1 实现

这个case比较简单,当给定的HTTP动词不存在时,直接返回nil, false即可:

router.go:

3.1.2 测试

router_test.go:

注:这里把testCases的类型改成了匿名结构体,不然没法用IDE的单个测试功能.单个测试功能如下图示:

单个测试功能

3.2 完全命中的情况

3.2.1 实现

  • step1. 切割path

    • 切割path时注意前导/和后置/

  • step2. 从根节点开始按"层次"(切割path后的字符串切片其实每个元素就是"一层")找子节点,找到了则继续深入一层

  • step3. 没找到就返回nil, false即可

router.go:

node.go:(此处只写新增的方法)

3.2.2 测试

router_test.go:

3.3 命中了,但找到的node中,handler是nil的情况

这个case只需要加一个测试用例即可,因为按照我们设计的findRoute(),在返回时并没有关注HandleFunc是否为nil

router_test.go:

3.4 根节点

3.4.1 测试

router_test.go:

没有通过的单测

3.4.2 修bug

断点调试
步进调试
步进调试-定位问题

可以看到,过滤掉/之后,切割path的结果不符合预期.

修复思路:对根节点做特殊处理

router.go:

3.5 没有找到path

这里也是先加测试用例,发现能通过,就可以了.

router_test.go

附录

TODO:要去看v2代码中的用例,然后再拿回来跑

Last updated