简单说下最近遇到因为go函数内联导致的单测mock问题:

假设我们现在有这样一个简单的求max函数:

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

然后我们使用gomonkey来mock掉这个函数(当然,这是为了演示,事实上我们mock掉的一般是rpc调用):

// TestMax
func TestMax(t *testing.T) {
	// mock掉max方法
	maxMock := ApplyFunc(max, func(_, _ int) int {
		return 1
	})
	defer maxMock.Reset()
	// assert
	Convey("testMax", t, func() {
		res := max(1, 2)
		So(res, ShouldEqual, 1)
	})
}

按照预期,我们将max()函数mock成永远返回1,这个单测是可以pass的。

然而它永远都跑不过,因为这里的max会返回2

这就引入今天要讨论的问题:内联

首先来说:

内联是什么?

内联就是把简短的函数在调用它的地方展开。在计算机发展历程的早期,这个优化是由程序员手动实现的。现在,内联已经成为编译过程中自动实现的基本优化过程的其中一步。

为什么编译过程会引入内联?

内联是高性能编程的一种重要手段。每个函数调用都有开销:创建栈帧,读写寄存器,这些开销可以通过内联避免。

我们使用命令

go run -gcflags="-m -m" main.go 


来查看刚才的函数,确认是否被内联

可以看到,Max函数确实是被展开了。

所以我们刚才在调用前mock掉的单测,是没办法生效的。原因是,后面的被测代码压根没有调用Max函数。。。

那么:

如何禁用内联?

编译时加上参数:

-gcflags=all=-l

所以我们这样执行刚才的单测,可以看到pass了~