在单元测试异步操作时设置自己的NDB Future对象值

时间:2014-08-20 23:20:31

标签: python-2.7 google-app-engine google-cloud-datastore app-engine-ndb

我正在为Google App Engine应用程序(Python)编写一些单元测试。我正在使用mock库来模拟NDB tasklet

但是,因为tasklet返回Future对象,我想知道如何实例化并使用我自己的自定义NDB Future对象,我可以将其作为mock的行为的一部分返回。如果没有这种能力,我完全不知道如何模拟ndb tasklet以验证在tasklet中调用了正确的方法。

在这个例子中。 async_datastore_update()用@ ndb.toplevel修饰。在这个函数内部,有一个我要模拟的NDB tasklet:yield self._async_get_specialist(specialist_name)

a_mgr = data_manager.AchievementsHandler()

# Initiate data store sync and flush async to force result
future = a_mgr.async_datastore_update()
future.get_result()  # Flushes out the self.async_get_specialist() tasklet's results

# Continue testing...

没有模拟self.async_get_specialist(),这可以通过刷新顶级函数中的异步进程来实现。

然而,当我嘲笑self.async_get_specialist()来验证行为时,我在调用未来的get_result()方法时遇到异常:

# Here, we mock out a future object, as per GAE docs
promise_obj_stub = ndb.Future()

a_mgr = data_manager.AchievementsHandler()
a_mgr._async_get_specialist = mock.Mock(return_value=promise_obj_stub) 

# Initiate data store sync and flush async to force result
future = a_mgr.async_datastore_update()
future.get_result()  # Throws a RuntimeError exception b/c result is never assigned to the Future object

App Engine docs似乎没有表明除了检查来自GAE api的结果之外,还有一种方法可以使用Future对象。即我在文档中看不到Future.set_return_value()的影响。

有人知道a)单元测试tasklets的最佳方法吗?或者b)假设我的方法有意义,如何将值传递给ndb Future对象?

2 个答案:

答案 0 :(得分:5)

哈哈!在使用ndb Future类之后,看起来Future实例有一个set_result()方法,可以将您自己的信息传递给将来(目前在GAE Future class docs上未列出。因此,以下代码可以正常工作奇妙:

# Here, we mock out a future object, as per GAE docs
promise_obj_stub = ndb.Future()

a_mgr = data_manager.AchievementsHandler()
a_mgr._async_get_specialist = mock.Mock(return_value=promise_obj_stub) 

# Initiate data store sync and flush async to force result
future = a_mgr.async_datastore_update()

promise_obj_stub.set_result(['list', 'of', 'results', 'i', 'needed'])  # set a result on the Future obj

future.get_result()  # Works like a charm now

# Continue testing as usual

答案 1 :(得分:1)

接受的答案效果很好,但我想添加我用于其他读者的方法。

我通常做的是创建一个像MockFuture这样的简单对象:

class MockFuture(object):
  def __init__(self, data=None):
    self.data = data

  def get_result(self):
    return self.data

这允许您在创建我发现更自然的未来时设置数据。

future = MockFuture(data=432)

另外一条评论,这在测试情况之外也很有用。有时你有一些条件逻辑,如果你实际上进行异步调用,你想要None(或其他)。在这种情况下,您可以使用MockFuture(也可以使用已接受的答案),以便处理结果的代码不必检查对象或类似的东西,它只是调用get_result()而不管事情是如何设置的