web-dev-qa-db-fra.com

Test unitaire d'une application python qui utilise la bibliothèque de requêtes

J'écris une application qui effectue des opérations REST en utilisant bibliothèque de requêtes de Kenneth Reitz et j'ai du mal à trouver une belle façon de tester ces applications, car les demandes fournissent ses via des méthodes au niveau du module.

Ce que je veux, c'est la capacité de synthétiser la conversation entre les deux parties; fournir une série d'assertions et de réponses aux demandes.

59
Chris R

Si vous utilisez spécifiquement des requêtes, essayez httmock . C'est merveilleusement simple et élégant:

from httmock import urlmatch, HTTMock
import requests

# define matcher:
@urlmatch(netloc=r'(.*\.)?google\.com$')
def google_mock(url, request):
    return 'Feeling lucky, punk?'

# open context to patch
with HTTMock(google_mock):
    # call requests
    r = requests.get('http://google.com/')
print r.content  # 'Feeling lucky, punk?'

Si vous voulez quelque chose de plus générique (par exemple pour se moquer de n'importe quelle bibliothèque faisant des appels http), optez pour httpretty .

Presque aussi élégant:

import requests
import httpretty

@httpretty.activate
def test_one():
    # define your patch:
    httpretty.register_uri(httpretty.GET, "http://yipit.com/",
                        body="Find the best daily deals")
    # use!
    response = requests.get('http://yipit.com')
    assert response.text == "Find the best daily deals"

HTTPretty est beaucoup plus riche en fonctionnalités - il propose également un code d'état moqueur, des réponses en streaming, des réponses tournantes, des réponses dynamiques (avec rappel).

42
Marek Brzóska

Il est en fait un peu étrange que la bibliothèque ait une page blanche sur les tests unitaires des utilisateurs finaux, tout en visant la convivialité et la facilité d'utilisation. Il existe cependant une bibliothèque facile à utiliser par Dropbox, sans surprise appelée responses . Voici son billet d'introduction . Il indique qu'ils n'ont pas réussi à utiliser httpretty , sans indiquer la raison de l'échec, et a écrit une bibliothèque avec une API similaire.

import unittest

import requests
import responses


class TestCase(unittest.TestCase):

  @responses.activate  
  def testExample(self):
    responses.add(**{
      'method'         : responses.GET,
      'url'            : 'http://example.com/api/123',
      'body'           : '{"error": "reason"}',
      'status'         : 404,
      'content_type'   : 'application/json',
      'adding_headers' : {'X-Foo': 'Bar'}
    })

    response = requests.get('http://example.com/api/123')

    self.assertEqual({'error': 'reason'}, response.json())
    self.assertEqual(404, response.status_code)
31
saaj

Vous pouvez utiliser une bibliothèque de simulation telle que Mocker pour intercepter les appels à la bibliothèque de requêtes et renvoyer les résultats spécifiés.

Comme exemple très simple, considérez cette classe qui utilise la bibliothèque de requêtes:

class MyReq(object):
    def doSomething(self):
        r = requests.get('https://api.github.com', auth=('user', 'pass'))
        return r.headers['content-type']

Voici un test unitaire qui intercepte l'appel à requests.get et renvoie un résultat spécifié pour le test:

import unittest
import requests
import myreq

from mocker import Mocker, MockerTestCase

class MyReqTests(MockerTestCase):
    def testSomething(self):
        # Create a mock result for the requests.get call
        result = self.mocker.mock()
        result.headers
        self.mocker.result({'content-type': 'mytest/pass'})

        # Use mocker to intercept the call to requests.get
        myget = self.mocker.replace("requests.get")
        myget('https://api.github.com', auth=('user', 'pass'))
        self.mocker.result(result)

        self.mocker.replay()

        # Now execute my code
        r = myreq.MyReq()
        v = r.doSomething()

        # and verify the results
        self.assertEqual(v, 'mytest/pass')
        self.mocker.verify()

if __== '__main__':
    unittest.main()

Lorsque j'exécute ce test unitaire, j'obtiens le résultat suivant:

.
----------------------------------------------------------------------
Ran 1 test in 0.004s

OK
23
srgerg

en utilisant moqueur comme dans la réponse de srgerg:

def replacer(method, endpoint, json_string):
    from mocker import Mocker, ANY, CONTAINS
    mocker = Mocker()
    result = mocker.mock()
    result.json()
    mocker.count(1, None)
    mocker.result(json_string)
    replacement = mocker.replace("requests." + method)
    replacement(CONTAINS(endpoint), params=ANY)
    self.mocker.result(result)
    self.mocker.replay()

Pour la bibliothèque de demandes, cela intercepterait la demande par méthode et point de terminaison que vous atteignez et remplacerait le .json () sur la réponse par le json_string transmis.

2
jchysk

Il manque request-mock .

De leur page:

>>> import requests
>>> import requests_mock

En tant que gestionnaire de contexte:

>>> with requests_mock.mock() as m:

...     m.get('http://test.com', text='data')
...     requests.get('http://test.com').text
...
'data'

Ou en tant que décorateur:

>>> @requests_mock.mock()
... def test_func(m):
...     m.get('http://test.com', text='data')
...     return requests.get('http://test.com').text
...
>>> test_func()
'data'
1
Unapiedra