3.08 路由树-通配符匹配之路由查找与测试
本节课工程结构如下:
(base) yanglei@yuanhong 13-findWildcard % tree ./
./
├── context.go
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── node.go
├── router.go
├── router_test.go
└── serverInterface.go
0 directories, 8 files
PART1. 修改childOf()
方法
childOf()
方法之前的childOf()
方法是仅在当前节点的子节点映射中查找子节点,但很明显现在需要再查找当前节点的通配符子节点.
node.go
:
// childOf 根据给定的path在当前节点的子节点映射中查找对应的子节点(即匹配到了静态路由)
// 若未在子节点映射中找到对应子节点 则尝试返回当前节点的通配符子节点
func (n *node) childOf(path string) (child *node, found bool) {
// 当前节点的子节点映射为空 则有可能匹配到通配符节点
if n.children == nil {
return n.wildcardChild, n.wildcardChild != nil
}
// 在子当前节点的节点映射中查找对应的子节点 若未找到同样尝试返回当前节点的通配符子节点
child, found = n.children[path]
if !found {
return n.wildcardChild, n.wildcardChild != nil
}
// 找到了对应的子节点 则返回该子节点
return child, found
}
PART2. 编写测试用例
这里还是在router_test.go
新建一个单独测试查找通配符路由的函数.
2.1 测试普通节点的通配符子节点
router_test.go
:
// TestRouter_findRoute_wildcard 测试针对通配符的路由查找功能
func TestRouter_findRoute_wildcard(t *testing.T) {
// step1. 构造路由树
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/order/*",
},
}
r := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
r.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 构造测试用例
testCases := []struct {
name string
method string
path string
isFound bool
wantNode *node
}{
// 普通节点的通配符子节点测试用例
{
name: "order wildcard",
method: http.MethodGet,
path: "/order/abc",
isFound: true,
wantNode: &node{
path: "*",
children: nil,
wildcardChild: nil,
HandleFunc: mockHandleFunc,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
foundNode, found := r.findRoute(testCase.method, testCase.path)
assert.Equal(t, testCase.isFound, found)
if !found {
return
}
msg, found := testCase.wantNode.equal(foundNode)
assert.True(t, found, msg)
})
}
}
测试顺利通过
2.2 测试普通节点下普通子节点和通配符子节点共存
router_test.go
:
// TestRouter_findRoute_wildcard 测试针对通配符的路由查找功能
func TestRouter_findRoute_wildcard(t *testing.T) {
// step1. 构造路由树
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/order/*",
},
{
method: http.MethodGet,
path: "/order/detail",
},
}
r := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
r.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 构造测试用例
testCases := []struct {
name string
method string
path string
isFound bool
wantNode *node
}{
// 普通节点的通配符子节点测试用例
{
name: "order wildcard",
method: http.MethodGet,
path: "/order/abc",
isFound: true,
wantNode: &node{
path: "*",
children: nil,
wildcardChild: nil,
HandleFunc: mockHandleFunc,
},
},
// 普通节点下普通子节点和通配符子节点共存的测试用例
{
name: "order detail",
method: http.MethodGet,
path: "/order/detail",
isFound: true,
wantNode: &node{
path: "detail",
children: nil,
wildcardChild: nil,
HandleFunc: mockHandleFunc,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
foundNode, found := r.findRoute(testCase.method, testCase.path)
assert.Equal(t, testCase.isFound, found)
if !found {
return
}
msg, found := testCase.wantNode.equal(foundNode)
assert.True(t, found, msg)
})
}
}
测试顺利通过
PART3. 测试Server的通配符匹配
httpServer_test.go
:
func TestServer_serve(t *testing.T) {
s := NewHTTPServer()
handleFunc := func(ctx *Context) {
// 直接调用http.ResponseWriter的Write方法时 默认响应码为200
ctx.Resp.Write([]byte("hello order detail"))
}
s.GET("/order/detail", handleFunc)
wildcardHandleFunc := func(ctx *Context) {
respPath := ""
pathSegments := strings.Split(ctx.Req.URL.Path, "/")
for _, pathSegment := range pathSegments {
if pathSegment == "" {
continue
}
respPath += pathSegment
respPath += " "
}
respPath = strings.TrimRight(respPath, " ")
respMsg := fmt.Sprintf("hello %s", respPath)
ctx.Resp.Write([]byte(respMsg))
}
s.GET("/order/*", wildcardHandleFunc)
_ = s.Start(":8081")
}
Last updated