4.02 课后复习-Route
本节课工程结构:
(base) yanglei@yuanhong v4-rc % tree ./
./
├── context.go
├── handle_func.go
├── server.go
├── server_interface.go
└── server_test.go
0 directories, 5 files
PART1. 定义路由森林与节点
1.1 定义节点
新建文件node.go
:
package v4_rc
// node 路由树中的节点
type node struct {
// pattern 路由路径
pattern string
// children 子节点 key为子节点的路由路径 value为路径对应子节点
children map[string]*node
// HandleFunc 路由对应的处理函数
HandleFunc
}
1.2 定义路由森林
新建文件router.go
:
package v4_rc
// router 路由森林
type router struct {
// trees 路由森林 key为HTTP动词 value为HTTP对应路由树的根节点
trees map[string]*node
}
1.3 定义添加路由的操作
router.go
:
package v4_rc
// router 路由森林
type router struct {
// trees 路由森林 key为HTTP动词 value为HTTP对应路由树的根节点
trees map[string]*node
}
// addRoute 添加路由
func (r *router) addRoute(method string, pattern string, handle HandleFunc) {
panic("implement me")
}
PART2. 组合路由森林与Server
server_interface.go
:
package v4_rc
import "net/http"
// ServerInterface 服务器实体接口
// 用于定义服务器实体的行为
type ServerInterface interface {
// Handler 组合http.Handler接口
http.Handler
// Start 启动服务器
Start(addr string) error
}
server.go
:
package v4_rc
import (
"net"
"net/http"
)
type Server struct {
*router
}
// ServeHTTP 是http.Handler接口的方法 此处必须先写个实现 不然Server不是http.Handler接口的实现
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := &Context{
Req: r,
Resp: w,
}
s.serve(ctx)
panic("implement me")
}
// serve 查找路由树并执行匹配到的节点所对应的处理函数
func (s *Server) serve(ctx *Context) {
panic("implement me")
}
// Start 启动服务器
func (s *Server) Start(addr string) error {
listener, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return http.Serve(listener, s)
}
// GET 注册GET路由
func (s *Server) GET(path string, handler HandleFunc) {
s.addRoute(http.MethodGet, path, handler)
}
// POST 注册POST路由
func (s *Server) POST(path string, handler HandleFunc) {
s.addRoute(http.MethodPost, path, handler)
}
PART3. 静态路由注册
3.1 编写测试方法
3.1.1 构造路由树
router_test.go
:
package v4_rc
import (
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
}
3.1.2 断言路由森林
若2个路由森林中的路由树数量不同,则不相等
如果目标router中没有对应HTTP动词的路由树(例:目标路由森林中有GET/POST/PUT这3棵路由树,而期望路由森林中有GET/POST/DELETE这3棵路由树),则不相等
router_test.go
:
package v4_rc
import (
"fmt"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg := fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// TODO: implement me
return "", false
}
3.1.3 断言路由树
若2个节点中有一个为nil,则不相等
若2个节点的path不同,则不相等
若2个节点的子节点数量不同,则不相等
若2个节点的HandleFunc类型不同,则不相等
比对2个节点的子节点映射:
若源节点的子节点映射中,存在目标节点中没有的子节点,则不相同
注:因为上边已经比对过2个节点的子节点数量了,所以只要能通过这个步骤的比对,那么2个节点的子节点必然是一一对应的
两个path相同的节点递归比对
router_test.go
:
package v4_rc
import (
"fmt"
"reflect"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg := fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
if !ok {
return msg, false
}
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// step1. 目标节点为空 则必然不等
if target == nil {
msg = "目标节点为nil"
return msg, false
}
// step2. 比较节点的路径是否相同
if n.path != target.path {
msg = fmt.Sprintf("节点的路径不等, 期望节点的路径为: %s, 目标节点的路径为: %s", n.path, target.path)
return msg, false
}
// step3. 比较2个节点的子节点数量是否相同
if len(n.children) != len(target.children) {
msg = fmt.Sprintf("节点的子节点数量不等, 期望节点的子节点数量为: %d, 目标节点的子节点数量为: %d", len(n.children), len(target.children))
return msg, false
}
// step4. 比较2个节点的处理函数是否相同
wantHandler := reflect.ValueOf(n.HandleFunc)
targetHandler := reflect.ValueOf(target.HandleFunc)
if wantHandler != targetHandler {
msg = fmt.Sprintf("节点的处理函数不等, 期望节点的处理函数为: %v, 目标节点的处理函数为: %v", wantHandler, targetHandler)
return msg, false
}
// step5. 比较2个节点的子节点是否相同
for path, child := range n.children {
// step5.1 比对2个节点的子节点的路径是否相同
dstChild, exist := target.children[path]
if !exist {
msg = fmt.Sprintf("目标节点中不存在路径为: %s 的子节点", path)
return msg, false
}
// step5.2 对路径相同的子节点递归比对
msg, equal := child.equal(dstChild)
if !equal {
return msg, false
}
}
return "", true
}
3.2 添加测试用例
router_test.go
:
package v4_rc
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"reflect"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/user/home",
},
}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 验证路由树
wantRouter := &router{
trees: map[string]*node{
http.MethodGet: &node{
path: "/",
children: map[string]*node{
"user": &node{
path: "user",
children: map[string]*node{
"home": &node{
path: "home",
children: nil,
HandleFunc: mockHandleFunc,
},
},
HandleFunc: nil,
},
},
HandleFunc: nil,
},
},
}
msg, ok := wantRouter.equal(targetRouter)
assert.True(t, ok, msg)
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg := fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
if !ok {
return msg, false
}
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// step1. 目标节点为空 则必然不等
if target == nil {
msg = "目标节点为nil"
return msg, false
}
// step2. 比较节点的路径是否相同
if n.path != target.path {
msg = fmt.Sprintf("节点的路径不等, 期望节点的路径为: %s, 目标节点的路径为: %s", n.path, target.path)
return msg, false
}
// step3. 比较2个节点的子节点数量是否相同
if len(n.children) != len(target.children) {
msg = fmt.Sprintf("节点的子节点数量不等, 期望节点的子节点数量为: %d, 目标节点的子节点数量为: %d", len(n.children), len(target.children))
return msg, false
}
// step4. 比较2个节点的处理函数是否相同
wantHandler := reflect.ValueOf(n.HandleFunc)
targetHandler := reflect.ValueOf(target.HandleFunc)
if wantHandler != targetHandler {
msg = fmt.Sprintf("节点的处理函数不等, 期望节点的处理函数为: %v, 目标节点的处理函数为: %v", wantHandler, targetHandler)
return msg, false
}
// step5. 比较2个节点的子节点是否相同
for path, child := range n.children {
// step5.1 比对2个节点的子节点的路径是否相同
dstChild, exist := target.children[path]
if !exist {
msg = fmt.Sprintf("目标节点中不存在路径为: %s 的子节点", path)
return msg, false
}
// step5.2 对路径相同的子节点递归比对
msg, equal := child.equal(dstChild)
if !equal {
return msg, false
}
}
return "", true
}
3.3 实现addRoute方法
3.3.1 根据method查找路由树,不存在则创建
router.go
:
package v4_rc
import (
"strings"
)
// router 路由森林
type router struct {
// trees 路由森林 key为HTTP动词 value为HTTP对应路由树的根节点
trees map[string]*node
}
func newRouter() *router {
return &router{
trees: map[string]*node{},
}
}
// addRoute 添加路由
func (r *router) addRoute(method string, path string, handle HandleFunc) {
// step1. 查找路由树,不存在则创建
root, exist := r.trees[method]
if !exist {
root = &node{
path: "/",
}
r.trees[method] = root
}
}
3.3.2 对根节点做特殊处理
此处要特殊处理根节点的原因在于:后续按/
分割path后,根节点就直接被分割没了(变成"")了.所以要特殊处理根节点
router.go
:
package v4_rc
import (
"strings"
)
// router 路由森林
type router struct {
// trees 路由森林 key为HTTP动词 value为HTTP对应路由树的根节点
trees map[string]*node
}
func newRouter() *router {
return &router{
trees: map[string]*node{},
}
}
// addRoute 添加路由
func (r *router) addRoute(method string, path string, handle HandleFunc) {
// step1. 查找路由树,不存在则创建
root, exist := r.trees[method]
if !exist {
root = &node{
path: "/",
}
r.trees[method] = root
}
// step2. 在根节点上查找子节点 不存在则创建
// step2.1 由于按/切割后 第一个元素为"" 也就是说如果传入的path为"/" 需要特殊处理
if path == "/" {
root.HandleFunc = handle
return
}
}
3.3.3 从根节点开始逐层查找目标节点,找到目标节点后添加HandleFunc
node.go
:
package v4_rc
// node 路由树中的节点
type node struct {
// path 路由路径
path string
// children 子节点 key为子节点的路由路径 value为路径对应子节点
children map[string]*node
// HandleFunc 路由对应的处理函数
HandleFunc
}
// findOrCreate 本方法用于根据给定的path值 在当前节点的子节点中查找path为给定path值的节点
// 找到则返回 未找到则创建
func (n *node) findOrCreate(segment string) *node {
if n.children == nil {
n.children = make(map[string]*node)
}
target, exist := n.children[segment]
if !exist {
// 当前节点的子节点映射中不存在目标子节点 则创建目标子节点 将子节点加入当前节点的子节点映射后返回
target = &node{
path: segment,
}
n.children[segment] = target
return target
}
// 当前节点的子节点映射中存在目标子节点 则直接返回
return target
}
router.go
:
package v4_rc
import (
"strings"
)
// router 路由森林
type router struct {
// trees 路由森林 key为HTTP动词 value为HTTP对应路由树的根节点
trees map[string]*node
}
func newRouter() *router {
return &router{
trees: map[string]*node{},
}
}
// addRoute 添加路由
func (r *router) addRoute(method string, path string, handle HandleFunc) {
// step1. 查找路由树,不存在则创建
root, exist := r.trees[method]
if !exist {
root = &node{
path: "/",
}
r.trees[method] = root
}
// step2. 在根节点上查找子节点 不存在则创建
// step2.1 由于按/切割后 第一个元素为"" 也就是说如果传入的path为"/" 需要特殊处理
if path == "/" {
root.HandleFunc = handle
return
}
// step2.2 从根节点开始 逐层查找
target := root
path = strings.TrimLeft(path, "/")
pathSegments := strings.Split(path, "/")
for _, pathSegment := range pathSegments {
// 在当前节点上查找子节点
child := target.findOrCreate(pathSegment)
target = child
}
// 为目标节点创建HandleFunc
target.HandleFunc = handle
}
此时运行测试用例即可顺利通过测试
3.4 测试用例
3.4.1 对根节点的测试用例
router_test.go
:
package v4_rc
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"reflect"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/user/home",
},
{
method: http.MethodGet,
path: "/",
},
}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 验证路由树
wantRouter := &router{
trees: map[string]*node{
http.MethodGet: &node{
path: "/",
children: map[string]*node{
"user": &node{
path: "user",
children: map[string]*node{
"home": &node{
path: "home",
children: nil,
HandleFunc: mockHandleFunc,
},
},
HandleFunc: nil,
},
},
HandleFunc: mockHandleFunc,
},
},
}
msg, ok := wantRouter.equal(targetRouter)
assert.True(t, ok, msg)
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg := fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
if !ok {
return msg, false
}
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// step1. 目标节点为空 则必然不等
if target == nil {
msg = "目标节点为nil"
return msg, false
}
// step2. 比较节点的路径是否相同
if n.path != target.path {
msg = fmt.Sprintf("节点的路径不等, 期望节点的路径为: %s, 目标节点的路径为: %s", n.path, target.path)
return msg, false
}
// step3. 比较2个节点的子节点数量是否相同
if len(n.children) != len(target.children) {
msg = fmt.Sprintf("节点的子节点数量不等, 期望节点的子节点数量为: %d, 目标节点的子节点数量为: %d", len(n.children), len(target.children))
return msg, false
}
// step4. 比较2个节点的处理函数是否相同
wantHandler := reflect.ValueOf(n.HandleFunc)
targetHandler := reflect.ValueOf(target.HandleFunc)
if wantHandler != targetHandler {
msg = fmt.Sprintf("节点的处理函数不等, 期望节点 %s 的处理函数为: %v, 目标节点 %s 的处理函数为: %v", n.path, wantHandler, target.path, targetHandler)
return msg, false
}
// step5. 比较2个节点的子节点是否相同
for path, child := range n.children {
// step5.1 比对2个节点的子节点的路径是否相同
dstChild, exist := target.children[path]
if !exist {
msg = fmt.Sprintf("目标节点中不存在路径为: %s 的子节点", path)
return msg, false
}
// step5.2 对路径相同的子节点递归比对
msg, equal := child.equal(dstChild)
if !equal {
return msg, false
}
}
return "", true
}
3.4.2 前导/user节点的测试用例
router_test.go
:
package v4_rc
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"reflect"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/",
},
{
method: http.MethodGet,
path: "/user",
},
{
method: http.MethodGet,
path: "/user/home",
},
}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 验证路由树
wantRouter := &router{
trees: map[string]*node{
http.MethodGet: &node{
path: "/",
children: map[string]*node{
"user": &node{
path: "user",
children: map[string]*node{
"home": &node{
path: "home",
children: nil,
HandleFunc: mockHandleFunc,
},
},
HandleFunc: mockHandleFunc,
},
},
HandleFunc: mockHandleFunc,
},
},
}
msg, ok := wantRouter.equal(targetRouter)
assert.True(t, ok, msg)
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg = fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
if !ok {
return msg, false
}
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// step1. 目标节点为空 则必然不等
if target == nil {
msg = "目标节点为nil"
return msg, false
}
// step2. 比较节点的路径是否相同
if n.path != target.path {
msg = fmt.Sprintf("节点的路径不等, 期望节点的路径为: %s, 目标节点的路径为: %s", n.path, target.path)
return msg, false
}
// step3. 比较2个节点的子节点数量是否相同
if len(n.children) != len(target.children) {
msg = fmt.Sprintf("节点的子节点数量不等, 期望节点的子节点数量为: %d, 目标节点的子节点数量为: %d", len(n.children), len(target.children))
return msg, false
}
// step4. 比较2个节点的处理函数是否相同
wantHandler := reflect.ValueOf(n.HandleFunc)
targetHandler := reflect.ValueOf(target.HandleFunc)
if wantHandler != targetHandler {
msg = fmt.Sprintf("节点的处理函数不等, 期望节点 %s 的处理函数为: %v, 目标节点 %s 的处理函数为: %v", n.path, wantHandler, target.path, targetHandler)
return msg, false
}
// step5. 比较2个节点的子节点是否相同
for path, child := range n.children {
// step5.1 比对2个节点的子节点的路径是否相同
dstChild, exist := target.children[path]
if !exist {
msg = fmt.Sprintf("目标节点中不存在路径为: %s 的子节点", path)
return msg, false
}
// step5.2 对路径相同的子节点递归比对
msg, equal := child.equal(dstChild)
if !equal {
return msg, false
}
}
return "", true
}
3.4.3 /order/detail节点的测试用例
为测试路由树中间有不存在的节点时,是否符合预期
router_test.go
:
package v4_rc
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"reflect"
"testing"
)
type TestNode struct {
method string
path string
}
func TestRouter_AddRoute(t *testing.T) {
// step1. 构造路由森林
testRoutes := []TestNode{
{
method: http.MethodGet,
path: "/",
},
{
method: http.MethodGet,
path: "/user",
},
{
method: http.MethodGet,
path: "/user/home",
},
{
method: http.MethodGet,
path: "/order/detail",
},
}
targetRouter := newRouter()
mockHandleFunc := func(ctx *Context) {}
for _, testRoute := range testRoutes {
targetRouter.addRoute(testRoute.method, testRoute.path, mockHandleFunc)
}
// step2. 验证路由树
wantRouter := &router{
trees: map[string]*node{
http.MethodGet: &node{
path: "/",
children: map[string]*node{
"user": &node{
path: "user",
children: map[string]*node{
"home": &node{
path: "home",
children: nil,
HandleFunc: mockHandleFunc,
},
},
HandleFunc: mockHandleFunc,
},
"order": &node{
path: "order",
children: map[string]*node{
"detail": &node{
path: "detail",
children: nil,
HandleFunc: mockHandleFunc,
},
},
HandleFunc: nil,
},
},
HandleFunc: mockHandleFunc,
},
},
}
msg, ok := wantRouter.equal(targetRouter)
assert.True(t, ok, msg)
}
func (r *router) equal(target *router) (msg string, ok bool) {
// step1. 比较路由森林中的路由树数量
wantLen := len(r.trees)
targetLen := len(target.trees)
if wantLen != targetLen {
msg = fmt.Sprintf("路由森林中的路由树数量不等, 期望路由树的数量为: %d, 目标路由树的数量为: %d", wantLen, targetLen)
return msg, false
}
// step2. 比对2个路由森林中的路由树HTTP动词是否相同
for method, tree := range r.trees {
dstTree, ok := target.trees[method]
if !ok {
msg = fmt.Sprintf("目标路由森林中不存在HTTP动词为: %s 的路由树", method)
return msg, false
}
// step3. 比对2个路由树中的结构是否相同
msg, ok = tree.equal(dstTree)
if !ok {
return msg, false
}
}
return "", true
}
func (n *node) equal(target *node) (msg string, ok bool) {
// step1. 目标节点为空 则必然不等
if target == nil {
msg = "目标节点为nil"
return msg, false
}
// step2. 比较节点的路径是否相同
if n.path != target.path {
msg = fmt.Sprintf("节点的路径不等, 期望节点的路径为: %s, 目标节点的路径为: %s", n.path, target.path)
return msg, false
}
// step3. 比较2个节点的子节点数量是否相同
if len(n.children) != len(target.children) {
msg = fmt.Sprintf("节点的子节点数量不等, 期望节点的子节点数量为: %d, 目标节点的子节点数量为: %d", len(n.children), len(target.children))
return msg, false
}
// step4. 比较2个节点的处理函数是否相同
wantHandler := reflect.ValueOf(n.HandleFunc)
targetHandler := reflect.ValueOf(target.HandleFunc)
if wantHandler != targetHandler {
msg = fmt.Sprintf("节点的处理函数不等, 期望节点 %s 的处理函数为: %v, 目标节点 %s 的处理函数为: %v", n.path, wantHandler, target.path, targetHandler)
return msg, false
}
// step5. 比较2个节点的子节点是否相同
for path, child := range n.children {
// step5.1 比对2个节点的子节点的路径是否相同
dstChild, exist := target.children[path]
if !exist {
msg = fmt.Sprintf("目标节点中不存在路径为: %s 的子节点", path)
return msg, false
}
// step5.2 对路径相同的子节点递归比对
msg, equal := child.equal(dstChild)
if !equal {
return msg, false
}
}
return "", true
}
3.4.4 其他HTTP动词的测试用例
a. /login
router_test.go
:
package v4_rc
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"reflect"