假设我们有这样一个方法:
func method(intr MyInterface) {
go intr.exec()
}
在单元测试method
中,我们想断言inter.exec
被调用了一次且仅调用了一次;所以我们可以在测试中用另一个模拟结构模拟它,这将为我们提供检查它是否被调用的功能:
type mockInterface struct{
CallCount int
}
func (m *mockInterface) exec() {
m.CallCount += 1
}
在单元测试中:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
现在,问题是因为 intr.exec
是用 go
关键字调用的,我们不能确定当我们在测试中到达我们的断言时,它是否被调用。
可能的解决方案 1:
为 intr.exec
的参数添加一个 channel 可以解决这个问题:我们可以等待在测试中从它接收任何对象,并且在从它接收到一个对象之后我们可以继续断言它是叫。该 channel 将完全不用于生产(非测试)代码。
这会起作用,但它会为非测试代码增加不必要的复杂性,并可能使大型代码库难以理解。
可能的解决方案 2:
在断言之前的测试中添加一个相对较小的 sleep 可以让我们确信 goroutine 将在 sleep 完成之前被调用:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
time.sleep(100 * time.Millisecond)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
这将使非测试代码保持原样。
问题是它会使测试变慢,并且会使它们不稳定,因为它们可能会在某些随机情况下中断。
可能的解决方案 3:
像这样创建效用函数:
var Go = func(function func()) {
go function()
}
然后像这样重写方法
:
func method(intr MyInterface) {
Go(intr.exec())
}
在测试中,我们可以将 Go
更改为:
var Go = func(function func()) {
function()
}
因此,当我们运行测试时,intr.exec
将被同步调用,我们可以确保我们的 mock 方法在断言之前被调用。
这个解决方案的唯一问题是它覆盖了 golang 的基本结构,这是不正确的做法。
这些是我能找到的解决方案,但据我所知,没有一个是令人满意的。什么是最佳解决方案?
最佳答案
在模拟中使用 sync.WaitGroup
您可以扩展 mockInterface
以允许它等待其他 goroutine 完成
type mockInterface struct{
wg sync.WaitGroup // create a wait group, this will allow you to block later
CallCount int
}
func (m *mockInterface) exec() {
m.wg.Done() // record the fact that you've got a call to exec
m.CallCount += 1
}
func (m *mockInterface) currentCount() int {
m.wg.Wait() // wait for all the call to happen. This will block until wg.Done() is called.
return m.CallCount
}
在测试中你可以做:
mock := &mockInterface{}
mock.wg.Add(1) // set up the fact that you want it to block until Done is called once.
method(mock)
if mock.currentCount() != 1 { // this line with block
// trimmed
}
关于unit-testing - 在 Golang 中进行单元测试时如何测试是否调用了 goroutine?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51065482/