integração de simulação e patch em um teste de unidade python
Tenho uma aula com alguns métodos para os quais estou escrevendo casos de teste de unidade. Para obter um exemplo reproduzível mínimo, estou anexando 3 dos métodos dessa classe:
Classe que estou testando métodos de:
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]
Minha classe de teste é a seguinte:
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.')
O que eu quero:
Eu gostaria de usar mock / patch em minha classe de teste sempre que possível. Isso provavelmente significa que MockGuard, MockSessione MockRequestser substituído por casos de simulação. Gostaria de ver como isso pode ser refinado para usar mock / patch do unittestpacote em python.
Respostas
Ok, tentando te dar uma ideia. Nos testes, você criou um addCookiemétodo falso para seus testes, mas só o usa para verificar como addCookiefoi chamado. Então, por exemplo, seu teste 3 e 4 você pode reescrever:
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
Em outros testes, você também pode ter que verificar os argumentos usados nas chamadas. Você sempre tem que definir o que realmente deseja testar e, em seguida, configurar seus mocks para que apenas essa funcionalidade seja testada.
Observe também que em alguns casos pode fazer sentido usar classes simuladas extras como você fez - não há nada de errado com isso, se funcionar melhor para você.