go每日一题:mock打桩、defer、recovery、panic的调用顺序

发布于:2024-12-08 ⋅ 阅读:(120) ⋅ 点赞:(0)

题目一:单元测试中使用—打桩

  • 打桩概念:使用A替换 原函数B,那么A就是打桩函数
  • 打桩原理:运行时,通过一个包,将内存中函数的地址替换为桩函数的地址
  • 打桩操作:利用Patch()函数,将调用外部依赖等函数(数据库等依赖函数),替换为自己写的函数,这样就是实现了不对外部数据的一个强依赖
  • 最简单的用法,官方解释:Monkey’s API is very simple and straightfoward. Call monkey.Patch(, ) to replace a function. For example:
package main

import (
	"fmt"
	"os"
	"strings"

	"bou.ke/monkey"
)

func main() {
	monkey.Patch(fmt.Println, func(a ...interface{}) (n int, err error) {
		s := make([]interface{}, len(a))
		for i, v := range a {
			s[i] = strings.Replace(fmt.Sprint(v), "hell", "*bleep*", -1)
		}
		return fmt.Fprintln(os.Stdout, s...)
	})
	fmt.Println("what the hell?") // what the *bleep*?
}

下面是对一个io文件的mock示例,文件为外部依赖,存在不稳定性质,因此使用mock

package test

import (
        "bou.ke/monkey"
        "bufio"
        "github.com/stretchr/testify/assert"
        "os"
        "strings"
        "testing"
)

func readFirstLine() string {
        open, err := os.Open("file.txt")
        defer open.Close()
        if err != nil {
                return ""
        }

        scanner := bufio.NewScanner(open)
        for scanner.Scan() {
                return scanner.Text()
        }
        return ""
}

func replaceLine() string {
        line := readFirstLine()
        afterReplace := strings.ReplaceAll(line, "11", "00")
        return afterReplace
}

func TestReplace(t *testing.T) {
        monkey.Patch(readFirstLine, func() string {  //注意是readFirstLine函数名,而不是readFirstLine()
                return "line11"
        })
        defer monkey.Unpatch(readFirstLine) // 注意是readFirstLine函数名,而不是readFirstLine()
        res := replaceLine()
        assert.Equal(t, "line00", res)
}

题二:defer、recovery、panic执行顺序

  • 准则:panic当前函数的下一行代码不会被执行到
  • 当 panic 被触发后,Go 语言的运行时机制会开始查找当前函数以及调用栈上的所有 defer 函数,然后依次执行它们。
  • 在这个过程中,如果某个 defer 函数中使用了 recover 函数,那么 recover 就可以捕获到之前触发的 panic 异常,恢复程序的正常执行流程从引发 panic 的函数返回,继续执行后续代码,注意是直接在panic那里return,panic当前函数的下面的代码依然不会被执行,但是其他后续代码可以执行
  • 并且 recover 会返回 panic 时传递的参数(在这里就是 “触发一个异常” 这个字符串),可以在 defer 函数中根据返回值进行相应的处理,比如打印异常信息等。
package main

import "fmt"

func inner() {
    defer func() {
        if r := recover(); r!= nil {
            fmt.Printf("inner函数中的defer通过recover捕获到异常,异常信息: %v\n", r)
        }
    }()
    panic("inner函数中触发异常")
    defer fmt.Println("这一行代码直接不会被执行")
}

func outer() {
    defer func() {
        fmt.Println("outer函数中的defer执行了")
    }()
    inner()
}

func main() {
    fmt.Println("程序开始执行")
    outer()  //其中发生了panic,如果没有recovery,下面的代码将不再执行
    fmt.Println("outer函数调用结束后,继续执行main函数后面的代码")
}

网站公告

今日签到

点亮在社区的每一天
去签到