1

我正在使用 MagicMock 测试 Web 应用程序中的功能。该函数是直接从模块导入的。

预期的行为是:当调用被测试的函数时,它会调用第三方 api(但我在为我的测试模拟这个)。这将返回一个字典,并且被测函数将其插入到一个对象中并返回该对象。

当我使用nosetests 运行特定的测试模块时,效果很好。

当我使用鼻子测试在我的test/unit/文件夹中发现和运行测试时,测试没有按预期工作。取而代之的是,模拟 API 返回 aNoneType并且被测试的函数返回一个 Magic Mock 实例。

考试:

def test_get_user_facebook_data_1(self):
    facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack@jack.jack', u'id': u'sd5Jtvtk6'}
    facepy.GraphAPI.get = MagicMock(return_value=facebook_oauth_response)

    user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')

    self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
    self.assertEquals(user_facebook_data._email, u'jack@jack.jack')
    self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')

正在测试的功能(在user_service模块中):

def get_user_facebook_data(facebook_access_token):

    '''
    With a user's FB access token, retrieve their credentials to either create a new account or login. Create a user object from the user model,         but don't save
    '''

    try:
        graph = facepy.GraphAPI(facebook_access_token)
        facebook_data = graph.get('me?fields=id,name,email')
    except facepy.exceptions.OAuthError:
        raise errors.FacebookAccessTokenInvalidError()

    user = user_model.User()

    try:
        facebook_oauth_id = facebook_data[u'id']
        user.set_new_fb_oauth(facebook_oauth_id)
    except KeyError:
        raise errors.OauthNoIdError()

    try:
        email = facebook_data[u'email']
        user.set_new_email(email)
    except KeyError:
        pass

    try:
        full_name = facebook_data[u'name']
        user.set_new_full_name(full_name)
    except KeyError:
        pass

    return user

你能帮我理解为什么结果不一致吗?

编辑

新信息 - 如果我直接在模块上使用鼻子测试,我正在测试的函数会以 unicode 的形式访问模拟的 Facepy 字典值(如预期的那样)。如果我使用nosetests 来发现测试,或者如果我使用下面dm03514 发布的解决方案并直接运行测试,则该函数会从模拟的facepy API 作为Magic Mock 实例访问字典。意思是,访问 dict 的每个结果都是一个 Magic Mock 实例。

这很令人困惑,因为我将 return_value(在所有测试中)设置为字典。

4

2 回答 2

2

抱歉,漫长的一天,所以无法真正从精神上分析为什么事情会以目前的方式运行:p

但是要解决它,使其无论在哪里执行测试都以相同的方式执行,就是在模块中修补 facepy导入。user_service

def test_get_user_facebook_data_1(self):
    facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack@jack.jack', u'id': u'sd5Jtvtk6'}
    with mock.patch('module.path.to.user_service.facepy') as mock_facepy:
      mock_facepy.GraphAPI.return_vaule.get = MagicMock(return_value=facebook_oauth_response)

      user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')

      self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
      self.assertEquals(user_facebook_data._email, u'jack@jack.jack')
      self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')

以上补丁facepy本地到user_service模块。

于 2017-03-06T01:54:10.627 回答
0

我的问题是对 MagicMock 处理字典的方式有误解。你需要声明它的__getitem__属性。

我认为我提到的“不一致”更像是我的测试完全有效的侥幸。

这大量借鉴了@dm03514 的回答

def test_get_user_facebook_data_1(self):
    facebook_oauth_response = {u'name': u'Jack Jacker', u'email': u'jack@jack.jack', u'id': u'sd5Jtvtk6'}
    with mock.patch('api.services.user_service.facepy') as mock_facepy:

        # Mocking the response from the facepy.
        # Setting this side effect allows the Mock object to be accessed as a dict.
        def getitem(name):
            return facebook_oauth_response[name]
        mock_oauth = MagicMock()
        mock_oauth.return_value = facebook_oauth_response 
        mock_oauth.__getitem__.side_effect = getitem
        mock_facepy.GraphAPI.return_value.get = mock_oauth

        user_facebook_data = user_service.get_user_facebook_data('bogus_facebook_oauth_access_token')

        self.assertEquals(user_facebook_data._facebook_oauth_id, u'sd5Jtvtk6')
        self.assertEquals(user_facebook_data._email, u'jack@jack.jack')
        self.assertEquals(user_facebook_data._full_name, u'Jack Jacker')
于 2017-03-07T12:09:18.850 回答