惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
爱范儿
爱范儿
人人都是产品经理
人人都是产品经理
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
量子位
罗磊的独立博客
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
小众软件
小众软件
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
大猫的无限游戏
大猫的无限游戏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园_首页
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
IT之家
IT之家
博客园 - 聂微东
Spread Privacy
Spread Privacy
V2EX - 技术
V2EX - 技术
S
Security Affairs
宝玉的分享
宝玉的分享
V
V2EX
C
Cisco Blogs
博客园 - Franky
美团技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
月光博客
月光博客
S
Securelist
J
Java Code Geeks
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
WordPress大学
WordPress大学
W
WeLiveSecurity
T
Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志

NickChenyx's Blog

Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish Create_Rubbish
Create_Rubbish
nickChen · 2018-04-18 · via NickChenyx's Blog

init 函数中发生 panic

在做单元测试的时候,当程序引入的包内有 init 函数并且抛出了 panic,如何修复这种场景?

第一反应肯定是能否 mock 掉出问题的函数,但是因为 mock 的执行顺序是在依赖包的 init 执行之后,所以 mock 生效前,init 函数就已经 panic 了。明显这样做是无效的。

以往的经验是在发生 panic 的 init 函数代码仓库中,新建一个测试分支,修改 init 中的逻辑避免 panic。然后再在单测代码中引入这个修复分支,才可以进行测试。在这里提供一个新的思路↓

利用 init 默认加载的顺序解决

新的方案,利用 init 加载顺序的机制,在前置运行的 init 函数中,mock 会发生 panic 的 init 函数场景。

我们尝试使用 init 加载顺序修复这个问题,如下的三个项目:

  1. import_panic_init 这个是苦主项目,他的引用了会产生 panic 的 init 函数的外部包
  2. panic_init 会产生 panic 的 init 的函数所在地
  3. util 无辜的工具库,被 panic_init 错误的使用产生了 panic
    .
    ├── import_panic_init
    │   ├── a
    │   │   └── a.go
    │   ├── go.mod
    │   ├── go.sum
    │   └── main.go
    ├── panic_init
    │   ├── go.mod
    │   └── panic_init.go
    └── util
     ├── go.mod
     └── util.go
    代码可以在仓库中获取:https://github.com/nickChenyx/code-repo/tree/main/golang/test_panic_init

在 util 包中,util.go 文件如下:

package util

import "fmt"

func IsTest(t *int) bool {
    fmt.Println("util.isTestCall")
    if t == nil {
        panic("t can't be nil")
    }
    return *t == 0
}

在 panic_init 包中,panic_init.go 文件如下:

package panic_init

import "fmt"
import "util"

func init() {
    util.IsTest(nil) // 必定发生 panic
    fmt.Println("panic_init run..")
}

在 import_panic_init 包中,main.go 文件如下:

package main

import (
        "fmt"
        _ "panic_init"
        "util"

        "bou.ke/monkey"
)

func main() {
        monkey.Patch(util.IsTest, func(t *int) bool {
                return true
        })
        fmt.Println("main run...")
}

可以看到此处 main 函数妄图 mock util.IsTest 函数,避免 panic 影响 fmt.Println(“main run…”) 的执行。

但是运行结果是:

$ go run main.go # 执行 import_panic_init.main 函数
util.isTestCall
panic: t can't be nil

goroutine 1 [running]:
util.IsTest(0x0)
        .../projects/test_panic_init/util/util.go:8 +0x89
panic_init.init.0()
        .../projects/test_panic_init/panic_init/panic_init.go:7 +0x1b    
exit status 2

可以看到 main 函数中的 mock 实际上未生效。修改 main.go 文件如下:

package main

import (
        _ "a" // 添加了这个 a 包,并且在 panic_init 包之前引入!这很重要
        "fmt"
        _ "panic_init"
        "util"

        "bou.ke/monkey"
)

func main() {
        monkey.Patch(util.IsTest, func(t *int) bool {
                return true
        })
        fmt.Println("main run...")
}

import_panic_init/a/a.go 中,定义了 mock 函数用于 mock util 包的函数如下:

package a 

import "util"
import "bou.ke/monkey"
import "fmt"

func init() {
    monkey.Patch(util.IsTest, func(t *int) bool {
    return true
    })
    fmt.Printf("a init call util.IsTest: %v\n", util.IsTest(nil))
}

然后再执行 main.go 如下:

$ go run main.go # 执行 import_panic_init.main
a init
a init call util.IsTest: true
panic_init run..
main run...

可以看到此时 a 包的 init 先于 panic_init 包的 init 执行,所以 mock 函数先被执行,panic_init 中的 util.IsTest 调用被 mock 返回 true,而不会发生 panic!

有趣的是,如果将 main.go 中 import 的顺序调整,那么依然会发生 panic:

import (
        "fmt"
        _ "panic_init"
        _ "a" // 在 panic_init 包之后引入,此时执行顺序在 panic_init 包之后
        "util"

        "bou.ke/monkey"
)

Golang 的执行顺序

import --> const --> var --> init()

一个 golang 文件中执行的顺序如上,先执行文件中定义的 import 包中的逻辑,再执行 const 常量定义,再执行 var 变量定义,再执行 init 函数。

具体可看:https://learnku.com/go/t/47135


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 nickchenyx@gmail.com