In diesem Artikel habe ich versucht, einige meiner Python-Testtechniken zu sammeln. Sie sollten sie nicht als Dogma betrachten, da ich denke, dass ich mit der Zeit meine Praktiken aktualisieren werde.
Ein bisschen Terminologie
Das Ziel ist das, was Sie gerade testen. Möglicherweise handelt es sich um eine Funktion, Methode oder ein Verhalten, das von einer Sammlung von Elementen generiert wird.
– , . , ( , ), – , , .
- . -. . , .
– , . -, «» .
,
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"