mengintegrasikan tiruan dan tambalan dalam pengujian unit python

Aug 20 2020

Saya memiliki kelas dengan beberapa metode yang saya tulis untuk kasus uji unit. Untuk contoh minimum yang dapat direproduksi, saya melampirkan 3 metode dari kelas itu:

Kelas yang saya uji metode:

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]

Kelas tes saya terlihat seperti berikut:

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

Apa yang saya inginkan:

Saya ingin menggunakan tiruan / tambalan di kelas pengujian saya di mana pun memungkinkan. Itu mungkin berarti begitu MockGuard, MockSessiondan MockRequestdiganti dengan contoh tiruan. Saya ingin melihat bagaimana ini dapat disempurnakan untuk menggunakan tiruan / patch dari unittestpaket dengan python.

Jawaban

1 MrBeanBremen Aug 20 2020 at 02:57

Oke, mencoba memberi Anda ide. Dalam pengujian, Anda telah membuat addCookiemetode palsu untuk pengujian Anda, tetapi Anda hanya menggunakannya untuk memeriksa cara addCookiedipanggil. Jadi, misalnya, tes 3 dan 4 Anda, Anda dapat menulis ulang:

   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

Dalam pengujian lain, Anda mungkin juga harus memeriksa argumen yang digunakan dalam panggilan. Anda selalu harus menentukan apa yang sebenarnya ingin Anda uji, dan kemudian mengatur tiruan Anda sehingga hanya fungsionalitas itu yang diuji.

Perhatikan juga bahwa dalam beberapa kasus mungkin masuk akal untuk menggunakan kelas tiruan tambahan seperti yang telah Anda lakukan - tidak ada yang salah dengan itu, jika itu yang terbaik untuk Anda.