在 python 单元测试中集成模拟和补丁 [英] integrating mock and patch in a python unit test

查看:47
本文介绍了在 python 单元测试中集成模拟和补丁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含一些方法的类,我正在为其编写单元测试用例.对于最小的可重现示例,我附上了该类中的 3 个方法:

I have a class with a few methods that I am writing unit test cases for. For minimum reproducible example, I am attaching 3 of the methods from that class:

我正在测试以下方法的类:

class WebViewLincSession(object):
    def renew_session_id(self, request):
            session = request.getSession()
            new_session_key = self.get_token()
            while new_session_key in session.guard.sessions:  # just in case the key is already used
                new_session_key = self.get_token()
            session.guard.sessions.pop(session.uid)  # remove the current session
            session.uid = new_session_key  # update the key
            session.guard.sessions[new_session_key] = session  # add session back with the new key
            request.addCookie(session.guard.cookieKey, new_session_key, path='/', secure=True, httpOnly=True)  # send updated cookie value
    def set_nonce(self, request):
        '''
        create a nonce value and send it as cookie
        '''
        if self._nonce_key is None:
            if self._NONCE_FOR_TEST:
                self._nonce_key = 'ecnon_for_test'
            else:
                self._nonce_key = 'ecnon_' + self.get_token()
            
        new_nonce_value = self.get_token()
        while new_nonce_value in self._nonce:  # just in case the value is already used
            new_nonce_value = self.get_token()
        
        now = time()
        stay_alive = now + self._STAY_ALIVE

        # reset timeout value for all existing nonces         
        for key in self._nonce.keys():
            if self._nonce[key] > stay_alive:
                self._nonce[key] = stay_alive
        
        self._nonce[new_nonce_value] = now + self._NONCE_TIMEOUT
        
        request.addCookie(self._nonce_key, new_nonce_value, path='/', secure=True, httpOnly=True)  # send updated cookie value
        
        return new_nonce_value


    def get_valid_nonce(self):
        now = time()
        return [nonce for nonce in self._nonce.keys() if self._nonce[nonce] > now]

我的测试类如下所示:

from __future__ import (division, absolute_import, with_statement)

from time import sleep

from mock import patch, MagicMock, mock, Mock
from requests.sessions import Session
from twisted.trial.unittest import TestCase

from viewlinc.webserver.web_viewlinc_session import WebViewLincSession


class MockGuard(object):
    '''Mock guard object for testing'''
    def __init__(self, *ags, **kwargs):
        ''' class constructor
        '''
        super(MockGuard, self).__init__(*ags, **kwargs)
        self.cookieKey = 'test_cookie_key'
        self.sessions = {'_test_session_': {}}

class MockSession(object):
    '''Mock session object for testing'''
    def __init__(self, *ags, **kwargs):
        ''' class constructor
        '''
        super(MockSession, self).__init__(*ags, **kwargs)
        self.guard = MockGuard()
        self.uid = '_test_session_'

class MockRequest(object):
    '''Mock Request object for testing'''
    def __init__(self, *ags, **kwargs):
        ''' class constructor
        '''
        super(MockRequest, self).__init__(*ags, **kwargs)
        self.session = MockSession()
        self.cookies = {}
  
    def getSession(self):
        ''' returns session object
        '''
        return self.session
          
    def addCookie(self, key, value, path='/', secure=True, httpOnly=True, expires=None):
        ''' add/replace cookie
        '''
        self.cookies[key] = {
            'value': value,
            'path': path,
            'secure': secure,
            'httpOnly': httpOnly,
            'expires': expires
        }
          
    def getCookie(self, key):
        ''' retrieve a cookie
        '''
        cookie = self.cookies.get(key, {'value': None})
        return cookie['value']

class WebViewLincSessionTests(TestCase):
    '''Test WebViewLincSession methods'''

    def __init__(self, *ags, **kwargs):
        ''' class constructor
        '''
        super(WebViewLincSessionTests, self).__init__(*ags, **kwargs)
        self.request = MockRequest()
        self.web_session = WebViewLincSession()
    


    def test_02_renew_session_id(self):
        '''Test renew_session_id
        '''
        self.web_session.renew_session_id(self.request)
        session = self.request.session
        return self.assertTrue(session.uid != '_test_session_' and session.uid in session.guard.sessions, 'renew_session_id failed')

    def test_03_set_nonce(self):
        '''Test set_nonce
        '''
        self.web_session.set_nonce(self.request)
        
        return self.assertTrue(len(self.request.cookies) > 0, 'set_nonce failed.')


    def test_04_get_valid_nonce(self):
        '''Test get_valid_nonce
        '''
        # use a clean session
        web_session = WebViewLincSession()
        web_session.set_nonce(self.request)
        web_session.set_nonce(self.request)
        valid_nonce = web_session.get_valid_nonce()

        self.assertTrue(len(valid_nonce) == 2, 'Expecting 2 valid nonces.')
        
        sleep(16)
        valid_nonce = web_session.get_valid_nonce()
        
        return self.assertTrue(len(valid_nonce) == 1, 'Expecting 1 valid nonce.')

我想要的:

我想尽可能在​​我的测试类中使用模拟/补丁.这可能意味着 MockGuardMockSessionMockRequest 被替换为模拟实例.我想看看如何改进以使用 python 中 unittest 包中的模拟/补丁.

I would like to use mock/patch in my test class where-ever possible. That probably means that MockGuard, MockSession and MockRequest be replaced with instances of mock. I would like to see how can this be refined to use mock/patch from unittest package in python.

推荐答案

好的,试着给你一个想法.在测试中,您为测试创建了一个伪造的 addCookie 方法,但您仅使用它来检查 addCookie 是如何被调用的.因此,例如,您可以重写测试 3 和 4:

Ok, trying to give you an idea. In the tests, you have created a fake addCookie method for your tests, but you only use it to check how addCookie has been called. So, for example, your test 3 and 4 you could rewrite:

   def test_03_set_nonce(self):
        request = mock.Mock()
        self.web_session.set_nonce(request)
        # we only need to know that it was called once
        request.addCookie.assert_called_once()

   def test_04_get_valid_nonce(self):
        request = mock.Mock()
        web_session = WebViewLincSession()
        web_session.set_nonce(request)
        web_session.set_nonce(request)
        # check that addCookie it has been called twice
        self.assertEqual(2, request.addCookie.call_count)
        
        valid_nonce = web_session.get_valid_nonce()
        ... # the rest is not dependent on mocks

在其他测试中,您可能还需要检查调用中使用的参数.您始终必须定义您实际想要测试的内容,然后设置您的模拟,以便仅测试该功能.

In other tests, you may have also to check the arguments used in the calls. You always have to define what you are actually want to test, and then setup your mocks so that only that functionality is tested.

另请注意,在某些情况下,像您所做的那样使用额外的模拟类可能是有意义的 - 如果这对您最有效,那没有任何问题.

Note also that in some cases it may make sense to use extra mock classes like you have done - there is nothing wrong with that, if that works best for you.

这篇关于在 python 单元测试中集成模拟和补丁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆