En este artículo, intenté recopilar algunas de mis técnicas de prueba de Python. No debes tomarlos como dogmas, porque creo que con el tiempo actualizaré mis prácticas.
Un poco de terminología
El objetivo es lo que está probando actualmente. Quizás sea una función, método o comportamiento generado por una colección de elementos.
– , . , ( , ), – , , .
- . -. . , .
– , . -, «» .
,
pytest. , . , transport.py test_transport.py.
, .
def refresh(...):
...
def test_refresh():
...
, , , :
def test_refresh_failure():
...
def test_refresh_with_timeout():
...
, , , , . , :
class Thing(object):
...
class TestThing(object):
def test_something(self):
...
test_constructor
, , test_default_state
:
def test_default_state(self):
credentials = self.make_credentials()
# No access token, so these shouldn't be valid.
assert not credentials.valid
# Expiration hasn't been set yet
assert not credentials.expired
# Scopes aren't required for these credentials
assert not credentials.requires_scopes
assert , ,
, . , : ( ) . . . , , . , .
, , , :
test_payload = {'test': 'value'}
encoded = jwt.encode(signer, test_payload)
expected_header = {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id}
expected_call = json.dumps(expected_header) + '.' + json.dumps(test_payload)
signer.sign.assert_called_once_with(expected_call)
, , , :
test_payload = {'test': 'value'}
encoded = jwt.encode(signer, test_payload)
header, payload, _, _ = jwt._unverified_decode(encoded)
assert payload == test_payload
assert header == {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id}
, assert_call
*, , , . , , , , , , . , ( ).
,
, - , , . , , .
, ( ):
signer = mock.create_autospec(crypt.Signer, instance=True)
signer.key_id = 1
test_payload = {'test': 'value'}
encoded = jwt.encode(signer, test_payload)
expected_header = {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id}
expected_call = json.dumps(expected_header) + '.' + json.dumps(test_payload)
signer.sign.assert_called_once_with(expected_call)
- , , :
signer = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1')
test_payload = {'test': 'value'}
encoded = jwt.encode(signer, test_payload)
header, payload, _, _ = jwt._unverified_decode(encoded)
assert payload == test_payload
assert header == {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id}
, , , , .
, Mock- . mock.create_autospec()
(https://docs.python.org/3/library/unittest.mock.html#unittest.mock.create_autospec) mock.patch(autospec=True)
(https://docs.python.org/3/library/unittest.mock.html#autospeccing), . , , , . , , , , , !
, , , :
signer = mock.Mock() encoded = jwt.encode(signer, test_payload) ... signer.sign.assert_called_once_with(expected_call)
, , . , , :
signer = mock.Mock(spec=['sign', 'key_id'])
encoded = jwt.encode(signer, test_payload)
...
signer.sign.assert_called_once_with(expected_call)
– mock.create_autospec()
mock.patch(..., autospec=True)
. , . , , :
signer = mock.create_autospec(crypt.Signer, instance=True)
encoded = jwt.encode(signer, test_payload)
...
signer.sign.assert_called_once_with(expected_call)
autospec, , , . , .
, , , , , , , (stub). , -, , , (, in-memory ).
, :
class CredentialsStub(google.auth.credentials.Credentials):
def __init__(self, token='token'):
super(CredentialsStub, self).__init__()
self.token = token
def apply(self, headers, token=None):
headers['authorization'] = self.token
def before_request(self, request, method, url, headers):
self.apply(headers)
def refresh(self, request):
self.token += '1'
Memcache:
class MemcacheFake(object):
def __init__(self):
self._data = {}
def set(self, key, value):
self._data[key] = value
def get(self, key):
return self._data.get(key)
, . , pylint, , , .
«»
, , , «». – , . Mock , wraps
:
credentials = mock.Mock(wraps=CredentialsStub())
...
assert credentials.refresh.called
//
, . mock_x, x_mock, mocked_x, fake_x ., x. , , , . , :
mock_signer = mock.create_autospec(crypt.Signer, instance=True)
signer:
signer = mock.create_autospec(crypt.Signer, instance=True)
patch , :
@mock.patch('google.auth._helpers.utcnow')
def test_refresh_success(mock_utcnow):
mock_utcnow.return_value = datetime.datetime.min
...
utcnow
:
@mock.patch('google.auth._helpers.utcnow')
def test_refresh_success(utcnow):
utcnow.return_value = datetime.datetime.min
...
patch
, x_patch
:
utcnow_patch = mock.patch('google.auth._helpers.utcnow')
with utcnow_patch as utcnow:
utcnow.return_value = datetime.datetime.min
...
, utcnow_patch
utcnow
. , , , .
, patch
, unused_x
:
@mock.patch('google.auth._helpers.utcnow')
def test_refresh_success(unused_utcnow):
...
helper-
. , helper- . , http-, :
def make_http(data, status=http_client.OK, headers=None):
response = mock.create_autospec(transport.Response, instance=True)
response.status = status
response.data = _helpers.to_bytes(data)
response.headers = headers or {}
http = mock.create_autospec(transport.Request)
http.return_value = response
return request
:
def test_refresh_success():
http = make_http('OK')
assert refresh(http)
def test_refresh_failure():
http = make_http('Not Found', status=http_client.NOT_FOUND)
with pytest.raises(exceptions.TransportError):
refresh(http)
pytest (https://docs.pytest.org/en/latest/fixture.html) – . , helper- , helper
. , , . , , - :
@pytest.fixture()
def server():
server = WSGIServer(application=TEST_APP)
server.start()
yield server
server.stop()
, . , , :
@pytest.fixture()
def database():
db = database.Client()
yield db
db.delete(db.list_all())
– , , . , . , , urllib3
transport
:
@pytest.fixture(params=['urllib3', 'requests'])
def http_request(request):
if request.param == 'urllib3':
yield google.auth.transport.urllib3.Request(URLLIB3_HTTP)
elif request.param == 'requests':
yield google.auth.transport.requests.Request(REQUESTS_SESSION)
2021 , Django . , «» Django Docker, , , . .
- "Python Developer. Professional"