简体   繁体   English

当鼻子测试发现测试时,魔术模拟失败

[英]Magic Mock fails when nosetest discovers tests

I'm using MagicMock to test a function in a web app. 我正在使用MagicMock来测试Web应用程序中的功能。 The function is imported directly from a module. 该功能直接从模块导入。

The expected behaviour is: when the function being tested is called, it calls on a third party api (but I'm mocking this for my test). 预期的行为是:调用被测试的函数时,它会调用第三方api(但是我正在为测试模拟)。 That returns a dictionary and the function under test inserts that into an object and returns the object. 这将返回一个字典,被测函数将其插入对象并返回该对象。

That works fine when I use nosetests to run that specific module of tests. 当我使用鼻子测试运行特定的测试模块时,这种方法可以正常工作。

When I use nosetests to discover and run tests in my test/unit/ folder, the test doesn't work as expected. 当我使用鼻子测试在test/unit/文件夹中发现并运行测试时,该测试无法正常工作。 Instead the mocked API returns a NoneType and the function being tested returns a Magic Mock instance. 取而代之的是, NoneType API返回一个NoneType ,而正在测试的函数返回一个Magic Mock实例。

The test: 考试:

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')

The function being tested (in user_service module): 正在测试的功能(在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

Can you please help me understand why the result is inconsistent? 您能帮我理解为什么结果不一致吗?

EDIT 编辑

New information - if I use nosetests on the module directly, the function I'm testing accesses the mocked Facepy dictionary values as unicode (as expected). 新信息-如果我直接在模块上使用鼻子测试,则我正在测试的函数将Unicode伪造的Facepy字典值(按预期方式)访问。 If I user nosetests to discover tests, or if I use the solution posted by dm03514 below and run the tests directly, the function accesses the dictionary from the mocked facepy API as Magic Mock instances. 如果我使用鼻子测试来发现测试,或者如果我使用dm03514下方发布的解决方案并直接运行测试,则该函数从模拟的facepy API作为Magic Mock实例访问字典。 Meaning, each result of accessing the dict is an Magic Mock instance. 意思是,访问字典的每个结果都是一个Magic Mock实例。

That's confusing, as I set the return_value (in all tests) to be the dictionary. 令人困惑,因为我将return_value(在所有测试中)设置为字典。

Sorry long day, so can't really mentally parse through why things are working the way they currently are :p 很抱歉,整天很抱歉,所以无法真正地从精神上分析为什么事情按目前的方式运作:p

But to solve it so it performs the same way regardless of where the test is executed, is to patch facepy import in the user_service module. 但是要解决此问题,以便无论在何处执行测试,它都以相同的方式执行操作,那就是在user_service模块中修补 facepy导入。

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')

The above patches the facepy local to user_service module. 这两个补丁的facepy本地user_service模块。

My issue was a misunderstanding about the way MagicMock handles dictionaries. 我的问题是对MagicMock处理字典的方式有误解。 You need to declare its __getitem__ property. 您需要声明其__getitem__属性。

I think the "inconsistency" I mentioned was more of being a fluke that my tests worked at all. 我认为我提到的“不一致”更多是因为我的测试完全奏效了。

This borrows heavily from @dm03514's answer . 这是从@ 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')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM