I've got a test that follows the following structure:
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.
}
The cleanupGoogleDatastore
method just runs a datastore Query for all entities of a specific entity kind, and then deletes them one by one. Source here:
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())
}
}
}
The problem I'm having is that records inserted after the "basic test fixtures" aren't deleted by this deferred cleanup statement.
If I change this test function to instead look like the following:
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.
}
then the newly created records are also deleted at the end of the test. My understanding was that deferred
functions are just called at the end of the original function scope, which would imply that the query wouldn't be constructed and run until the end of the test, but that doesn't seem to be the case here. It seems like the query is being constructed when the defer statement is called and then executed at the end of the test.
I've tried moving cleanupGoogleDatastore
into a closure (e.g. defer func() { cleanupGoogleDatastore(t, testInstance }()
and that didn't change anything.
I suspect that somehow this is a case of the function arguments being evaluated at the point where I call defer instead of when the function is invoked, but since both t
and testInstance
are pointers rather than direct values I'm not sure how that could be happening. There's no other evidence that the two values have changed. Printing out the fields of both structs at the different logical evaluation points doesn't reveal anything new.
What's going on here?
This turned out to be a deliberate property of the local App Engine Datastore stub, which returns weakly consistent queries by default. I was able to resolve this by changing the code for cleanupGoogleDatastore
to rely on an ancestor query, which successfully found and then deleted all of the entities. An alternative approach would have been to set aetest.Instance to be strongly consistent, but I didn't want to force strongly consistent behavior on all of my tests - just this one.