Una guía para mi estilo de prueba de Python

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"








All Articles