使用nosegae进行数据存储测试的意外测试结果

时间:2014-12-30 11:45:40

标签: python google-app-engine google-cloud-datastore nose nose-gae

我有一个UserRepository类,我正在为

编写单元测试
class UserRepository(object):
    """
    Repository that handles storage and retrieval of models.User objects
    in and from the datastore.

    """
    def create(self, user):
        """
        Create the given user in the datastore if it doesn't exist yet.

        Args:
            user: The user to create.

        Returns:
            The created user.

        Raises:
            exc.DuplicateEntity: If the desired phonenumber is
                already taken.

        """
        duplicate_user = models.User.query(models.User.phonenumber == user.phonenumber).fetch()
        if duplicate_user:
            raise exc.DuplicateEntity()

        user.put()
        return user

我有这些测试

class UserServiceTest(unittest.TestCase):
    """Tests for the UserService."""
    def setUp(self):
        """
        Called before tests are run.

        """
        self.user_repo = repositories.UserRepository()
        #self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1)

    def test_create(self):
        """
        Test if the create method creates a user.

        """
        ndb.delete_multi(models.User.query().fetch(keys_only=True))

        user = models.User(phonenumber='+31612345678#',
                           email='tim@castelijns.nl',
                           password='1234abcd')

        ret_user = self.user_repo.create(user)
        self.assertEqual(ret_user, user)

    def test_create_duplicate_fails(self):
        """
        Test if attempting to create a user with an existing phonenumber
        fails.

        """
        ndb.delete_multi(models.User.query().fetch(keys_only=True))

        user = models.User(phonenumber='+31612345678#',
                           email='tim@castelijns.nl',
                           password='1234abcd')

        self.user_repo.create(user)

        with self.assertRaises(exc.DuplicateEntity):
            self.user_repo.create(user)

ndb.delete_multi(models.User.query().fetch(keys_only=True))用于清除测试环境中的现有用户,因此测试用例不会相互影响。

这是自定义异常

class DuplicateEntity(Exception):
    """Exception to raise when trying to create a duplicate entity."""

我用

运行测试
$ nosetests --with-gae

输出

======================================================================
FAIL: Test if attempting to create a user with an existing phonenumber
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tests/dal/test_repositories.py", line 53, in test_create_duplicate_fails
    self.user_repo.create(user)
AssertionError: DuplicateEntity not raised

----------------------------------------------------------------------
Ran 2 tests in 0.080s

FAILED (failures=1)

这是意料之外的,因为此处对.create的第二次调用应该引发异常,因为已经有一个用户拥有该phonenumber。

我确信代码有效,因为我已经对它进行了实时测试。

如果我在with语句之上添加对.create的调用,它会引发异常,这也很奇怪:

self.user_repo.create(user)
self.user_repo.create(user)

with self.assertRaises(exc.DuplicateEntity):
    self.user_repo.create(user)

所以它在第3次通话时被提出,但不是第2次。

我感觉它与数据存储一致性策略有关,正如文档here所述:

  

PseudoRandomHRConsistencyPolicy类允许您控制   在每个全球(非祖先)之前申请写入的可能性   查询。通过将概率设置为0%,我们正在指示   数据存储存根以最大的最终数量运行   一致性。最大的最终一致性意味着写入将提交但是   始终无法应用,因此全局(非祖先)查询将会   始终没有看到变化。

然而,我不知道nosegae如何处理这个问题。它甚至可以配置吗? nosegae doesn't have alot of documentation

我该如何解决(或修复)这个问题?

1 个答案:

答案 0 :(得分:2)

您的问题是您正在使用查询来测试重复项,并且由于最终的一致性而无法保证能够正常运行。您将注意到,您引用的文档中的测试都使用了祖先查询,这些查询确保了一致性。

我的看法是这样,它是预期和正确的行为(AssertionError:DuplicateEntity not raise error)并突出显示模型/方法的问题。