In this article, I tried to collect a few of my Python testing techniques. Do not take them as dogma, because I think over time I will update my practices.
A bit of terminology
The target is what you are currently testing. Perhaps it is a function, method, or behavior generated by a collection of elements.
โ , . , ( , ), โ , , .
- . -. . , .
โ , . -, ยซยป .
,
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"