Test di un client REST su un server REST. Come si fa il calendario?


10

Quando si scrivono unit test, è comune usare dispositivi: pochi dati verificabili, quindi possiamo dire: 1. Ottenere tutti i clienti dovrebbero includere Willy Wonka. 2. Elimina il client 3 e ora ottieni che i client non dovrebbero più includere Willy Wonka.

Va bene per i test unitari. Utilizzare setup / teardown per ricaricare le partite o ripristinare la transazione. Quindi i test creati, gli aggiornamenti e le eliminazioni vengono eseguiti all'interno di una transazione . I nuovi dati temporanei durano solo per la durata del test, quindi vengono ripristinati.

Ma che dire di quando abbiamo separato il server REST dal client REST?

Vogliamo assicurarci che il nostro client REST non stia solo leggendo correttamente, ma creando, aggiornando ed eliminando correttamente.

Non sono stato in grado di trovare esempi o suggerimenti su come eseguire questa operazione su un server REST di test remoto.

Supponendo di avere un server REST di prova che serve solo dispositivi. L'intera natura apolide di HTTP significa che sarebbe difficile inviare un messaggio di tipo "INIZIA TRANSAZIONE" e "ROLLBACK TRANSACTION" o "RELOAD FIXTURES", giusto?

Non posso essere il primo a volerlo fare, quindi ho la sensazione di aver bisogno di un modo diverso di pensare a questo.

Eventuali suggerimenti?


Forse, dato che si tratta di un server di prova, è possibile avere un endpoint che ricaricherà i dispositivi?
David Radcliffe

Se il tuo problema principale è riportare il tuo server di prova in uno stato predefinito, perché non aggiungi alcuni tipi di funzioni di test speciali come "RELOAD TESTDATA" all'API di riposo per fare quello che vuoi? Ovviamente, dovresti assicurarti che quel tipo di chiamate API non sia disponibile in produzione.
Doc Brown

Risposte:


7

I sistemi software hanno idealmente confini e interfacce di sistema ben definiti tra loro. I servizi REST ne sono un buon esempio.

A tal fine, raccomanderei di non fare quello che stai cercando di fare.

In particolare:

Vogliamo assicurarci che il nostro client REST non stia solo leggendo correttamente, ma creando, aggiornando ed eliminando correttamente.

Quello che suggerirei invece è:

  • Creazione di test per il client REST, per assicurarsi che si comporti correttamente, dati input e output specifici. Tieni conto dei valori buoni (previsti) e negativi (imprevisti).

  • Creazione di test per il servizio REST (se lo controlli, vale a dire), per comportarsi in base alla funzione prevista

  • Mantenere i test vicini al loro dominio problematico, in modo che possano aiutare a guidare la progettazione e lo sviluppo di ciò che è importante in quel contesto


3
Respingi l'idea dei test di integrazione in modo abbastanza casuale. Non penso che questo approccio sia informato dalla pratica.
febbraio 913

Grazie a tutti i suggerimenti utili. Anche da Twitter ho ricevuto ottimi suggerimenti per provare la gemma "webmock" di Ruby e simile a deridere la risposta del server API REST. Concordo anche con "febeling" sul fatto che ciò che sto descrivendo sembra essere più un test di integrazione, quindi esaminerò separatamente questo. Grazie ancora a tutti. - Derek
consegna il

deridere un'API è un ottimo modo per risolvere il problema. Ma come ti assicuri che l'API derisa == API reale?
FrEaKmAn

4

Due angoli da tenere a mente qui:

  • Stai testando il tuo codice o l'impianto idraulico? Presumendo che tu stia utilizzando un servizio ben noto e uno stack client, puoi probabilmente presumere in modo sicuro i loro tester e migliaia di utenti generalmente assicureranno che non ci sia un bug fondamentale nelle basi.
  • Perché i tuoi test non sono idempotenti? Creare un modo per scrivere dati non di produzione o scrivere su un endpoint diverso. Scegli un modello di denominazione prevedibile. Pre-caricare il DB del server di riposo prima dei test. E probabilmente ci sono altri modi per farlo accadere: il metodo è davvero tattico e dovrebbe dipendere dalla natura dell'app.


1

Come altri hanno già detto, se si sta testando un client non è necessario spingersi fino a creare, eliminare, ecc. Sul server. Molte volte non hai nemmeno bisogno di deridere un server. Devi solo assicurarti di fare le richieste giuste e gestire correttamente le risposte, sia che siano scritte in Ruby, Python, PHP o qualsiasi altra cosa, ad un certo punto il tuo client probabilmente utilizzerà un metodo da una libreria HTTP per creare una richiesta ed è sufficiente per deridere quel metodo, controllare come viene chiamato e restituire un risultato del test.

Prendi un ipotetico client Python che usa urllib2per fare richieste. Probabilmente hai un metodo nel client, chiamiamolo get(), che contiene una chiamata urllib2.Request(). Devi solo deridere la chiamata alla tua classe get().

@patch('your.Client.get')
def test_with_mock(self, your_mock):
    your_mock.return_value({'some': 'json'})
    test_obj = your.Client.get_object(5)
    your_mock.assert_called_with('/the/correct/endpoint/5')

Questo esempio molto semplificato utilizza la libreria Mock di Python per testare una your.Clientclasse ipotetica con un get_object()metodo che genera l'URL corretto per ottenere qualcosa da alcune API. Per effettuare la richiesta, il client chiama il suo get()metodo con quell'URL. Qui, quel metodo è deriso ( your.Client.getè "patchato" in modo che sia sotto il controllo di your_mock) e il test controlla se è stato richiesto l'endpoint giusto.

Il metodo deriso restituisce la risposta JSON configurata ( your_mock.return_value) che il client deve gestire e faresti ulteriori affermazioni per testare che ha gestito i dati previsti nel modo previsto.


Ma come sei sicuro che quando stai facendo la richiesta "giusta" che è una richiesta effettiva (in produzione)? Perché se capisco il tuo suggerimento, se l'API cambia o si interrompe, i tuoi test continueranno a funzionare. Mentre in produzione è una storia completamente diversa.
FrEaKmAn

1

Quello che descrivi è uno scenario di test di integrazione. Questi di solito sono un po 'imbarazzanti da installare e demolire. Li rende lenti e abbastanza spesso fragili.

L'approccio con i dispositivi è altrettanto imbarazzante e goffo, ma è il modo predefinito in cui alcuni framework lo utilizzano, ad esempio Rails, ed è già supportato. Hanno bisogno del test case astratto o qualcosa di simile per preparare il database con i dispositivi. (Fai attenzione alla denominazione insolita delle categorie di test in Rails, i test unitari con i dispositivi DB stanno parlando strettamente anche dei test di integrazione.)

Il modo in cui vorrei andare sul tuo scenario è accettare di avere un controllo specifico del test sullo stato dell'applicazione API o sul suo database. È possibile disporre di endpoint aggiuntivi per l'installazione e lo smontaggio, che sono presenti solo nell'ambiente di test. O in alternativa, parli con il database (o qualunque cosa tu stia utilizzando) dietro la parte posteriore della tua applicazione / API.

Se ritieni che questo sia uno sforzo eccessivo (nel senso di indebito), allora considera che l'approccio con dispositivi per database fa proprio questo: usare ulteriori strumenti specifici per test per manipolare il database o lo stato dell'applicazione.

Tuttavia, non credo che questa discussione abbia a che fare con la natura apolide dell'HTTP. HTTP è senza stato, ma l'applicazione non lo è, nella maggior parte dei casi. Sembra un po 'come te dove cerchi la rigidità REST. Potresti anche avere tutte le risorse completamente creabili, leggibili ed eliminabili. In tal caso, potresti semplicemente eseguire tutte le operazioni di configurazione e smontaggio tramite i normali mezzi API. Spesso ciò non viene fatto in pratica, poiché non si desidera includere determinate operazioni da una comprensione aziendale dell'applicazione, almeno non al di fuori dell'ambiente di test.


1

Patch di scimmia

Nel mio lavoro facciamo ATDD usando un framework xUnit in uscita e chiamate di rete di patching tra scimmia e client. Nello stesso spazio di processo cariciamo il client, scimmia patch la chiamata di rete all'inizio del codice stack del server REST. Tutte le chiamate vengono quindi emesse dal client come sarebbero normalmente e il codice del server riceve le richieste esattamente come apparirebbero normalmente.

Benefici

  • non è necessario eseguire la sincronizzazione con l'avvio del server (perché non esiste un server)
  • sfruttare la classica impostazione dell'unità e il metodo di smontaggio per gestire cose come gli apparecchi
  • capacità di usare mock / stub e altre patch di scimmia per un controllo più fine del test
  • può essere scritto usando un framwork xUnit

compromessi

  • non espone interazioni / problemi multi-processo (blocco, fame di risorse, ecc.)
  • non espone problemi multi-server (serializzazione dei dati, stile di clustering)
  • non espone problemi di rete perché è simulato (accesso, errori di timeout, ecc.)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.