2012-03-04 3 views
49

Sto scrivendo un'applicazione che esegue operazioni REST utilizzando requests library di Kenneth Reitz e sto faticando a trovare un buon modo per testare queste applicazioni, perché le richieste forniscono i suoi metodi tramite modulo- metodi di livello.Unit testing di un'applicazione python che utilizza la libreria delle richieste

Quello che voglio è la capacità di sintetizzare la conversazione tra le due parti; fornire una serie di asserzioni e risposte di richieste.

+2

Quindi è necessario prendere in giro il resto server? – kgr

+0

perché ciò rende l'unittest inadatto? Controlla come la biblioteca esegue i propri test unitari; potrebbe offrire idee. –

+1

FWIW, la libreria Richieste esegue i propri test utilizzando URL live (github.com, il dominio degli autori, ecc.). – Raumkraut

risposta

21

È possibile utilizzare una libreria di simulazione come Mocker per intercettare le chiamate alla libreria di richieste e restituire risultati specificati.

Come un esempio molto semplice, prendere in considerazione questa classe che usa la libreria richieste:

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

Ecco uno unit test che intercetta la chiamata a requests.get e restituisce un risultato specifico per i 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 __name__ == '__main__': 
    unittest.main() 

Quando eseguo questo test dell'unità, ottengo il seguente risultato:

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

OK 
2

utilizzando beffardo come nella risposta di 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() 

Per la libreria richieste, questo sarebbe intercettare la richiesta di metodo e endpoint si sta colpendo e sostituire il .json() sulla risposta con la json_string passato in

.
38

Se si utilizzano richieste specifiche, provare httmock. E 'meravigliosamente semplice ed elegante:

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?' 

Se volete qualcosa di più generico (ad esempio per prendere in giro tutte le chiamate HTTP biblioteca rendendo) andare per httpretty.

Quasi come elegante:

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 è molto più ricco di funzionalità - ma offre anche beffardo codice di stato, le risposte in streaming, le risposte a rotazione, risposte dinamiche (con una richiamata).

+0

Questa piccola cosa è direttamente da questo mondo, grazie! – mccc

19

È in effetti un po 'strano che la libreria abbia una pagina vuota sui test delle unità degli utenti finali, pur puntando sulla semplicità d'uso e sulla facilità d'uso. Esiste tuttavia una libreria facile da usare di Dropbox, che non sorprende chiamare responses. Ecco il suo intro post. Dice che non sono riusciti a impiegare httpretty, senza dichiarare alcun motivo di errore, e ha scritto una libreria con API simili.

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) 
+0

URL aggiornato per 'responses' [** intro post **] (http://cra.mr/2014/05/20/mocking-requests-with-responses) –

0

manca da queste risposte è requests-mock.

Dalla loro pagina:

>>> import requests 
>>> import requests_mock 

Come un contesto manager:

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

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

O come decoratore:

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

Hai qualche conoscenza su come fare questo funziona con '' pytest''? Ho provato l'esatto esempio che stai citando. Rif .: https://stackoverflow.com/questions/47703748/why-does-the-simplest-requests-mock-example-fail-with-pytest –

+0

Se ricordo male, ho usato il decoratore. E penso che questo abbia funzionato anche con pytest. – Unapiedra

+0

L'ho fatto funzionare con i decoratori, ma sembra (sul mio sistema) che sia in conflitto con qualche altro argomento, quindi ho dovuto passare un argomento kw al Mocker, come menzionato in [docs] (https: // requests- mock.readthedocs.io/en/latest/mocker.html#decorator). Non sono sicuro se questo ha a che fare con pytest, ma l'errore che si è verificato ha riguardato i dispositivi. Grazie per tornare al problema. –