【发布时间】:2018-05-26 20:58:45
【问题描述】:
我有一个遵循以下结构的测试:
var testInstance aetest.Instance // initialized by a TestMain
TestThing(t *testing.T) {
defer cleanupGoogleDatastore(t, testInstance)
// insert basic test fixtures
// insert some new records here
// test assertions, etc.
}
cleanupGoogleDatastore 方法只是针对特定实体类型的所有实体运行数据存储查询,然后将它们一一删除。来源:
func cleanupGoogleDatastore(t *testing.T, testInstance aetest.Instance) {
q := datastore.NewQuery("Order")
ctx := GetContext(t, testInstance)
scanner := q.Run(ctx)
for {
var o model.Order
key, err := scanner.Next(&o)
if err == datastore.Done {
return
}
if err != nil {
t.Fatal(err.Error())
}
err = datastore.Delete(ctx, key)
if err != nil {
t.Fatal(err.Error())
}
}
}
我遇到的问题是,在“基本测试装置”之后插入的记录不会被这个延迟清理语句删除。
如果我将此测试函数更改为如下所示:
var testInstance aetest.Instance // initialized by a TestMain
TestThing(t *testing.T) {
defer cleanupGoogleDatastore(t, testInstance)
// insert basic test fixtures
// insert some new records here
defer cleanupGoogleDatastore(t, testInstance)
// note that I have to call it TWICE - just moving it here is not enough.
// test assertions, etc.
}
那么新创建的记录也会在测试结束时被删除。我的理解是 deferred 函数只是在原始函数作用域的末尾被调用,这意味着在测试结束之前不会构造和运行查询,但情况似乎并非如此这里。似乎查询是在调用 defer 语句时构建的,然后在测试结束时执行。
我尝试将 cleanupGoogleDatastore 移动到闭包中(例如 defer func() { cleanupGoogleDatastore(t, testInstance }() 并没有改变任何东西。
我怀疑这是在我调用 defer 而不是在调用函数时评估函数参数的情况,但由于 t 和 testInstance 都是指针而不是直接值我'我不确定这是怎么发生的。没有其他证据表明这两个值发生了变化。在不同的逻辑评估点打印出两个结构的字段并没有揭示任何新内容。
这是怎么回事?
【问题讨论】:
-
这是你的清理函数的实际签名还是你传递一些参数给它?
-
还是定义在非指针值上的方法?
-
@mkopriva 这不是实际的签名。实际签名采用 *testing.T 和 aetest.Instance。我更新了代码示例以更准确地反映实际代码。
-
@mkopriva 暗示的是函数参数是在
defer处确定的,而不是在执行时确定的。一切都是按值传递的,因此在defer处创建一个副本,以供最终执行时使用。 (尽管不要混淆指针,指针值被复制,但它仍然指向反映变化的同一事物)请参阅示例以了解一些陷阱。 play.golang.org/p/2LoG_90OiSM -
@Venantius 一个简单的解决方法是将清理函数的调用包装到一个闭包中,这样在执行延迟函数时就会评估参数。
defer func() { cleanup(arg1, arg2) }()
标签: google-app-engine go google-cloud-datastore