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()方法是仅在当前节点的子节点映射中查找子节点,但很明显现在需要再查找当前节点的通配符子节点.

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