Test di un client REST contro un server REST. Come fare infissi?

10

Durante la scrittura dei test delle unità, è normale utilizzare i dispositivi di fissaggio: pochi dati testabili, quindi possiamo dire: 1. Ottenere tutti i clienti dovrebbero includere Willy Wonka. 2. Elimina il client 3 e ora i client non dovrebbero più includere Willy Wonka.

Va bene per i test unitari. Usa setup / teardown per ricaricare i dispositivi o eseguire il rollback della transazione. Pertanto, i test di creazione, aggiornamento ed eliminazione vengono eseguiti all'interno di una transazione . I nuovi dati temporanei durano solo per la durata del test, quindi vengono ripristinati.

Ma 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 per come eseguire questa operazione contro un server REST di test remoto.

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

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

Qualche suggerimento?

    
posta sivers 09.03.2013 - 02:34
fonte

6 risposte

7

I sistemi software hanno idealmente confini di sistema ben definiti e interfacce tra di loro. I servizi REST sono buoni esempi di questo.

A tal fine, consiglierei contro di fare ciò che stai cercando di fare.

In particolare:

We want to make sure our REST client is not just reading correctly, but creating, updating, and deleting correctly.

Quello che suggerirei è invece:

  • Costruire test per il tuo client REST, per assicurarti che si comporti correttamente, dato input e output specifici. Valori positivi (previsti) e negativi (inattesi).

  • Costruire test per il tuo servizio REST (se lo controlli, cioè), per comportarti in base alla funzione prevista

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

risposta data 09.03.2013 - 03:05
fonte
4

Due angoli da tenere a mente qui:

  • Stai testando il tuo codice o l'impianto idraulico? Supponendo che tu stia utilizzando un servizio ben noto e stack di client puoi probabilmente presumere che i loro tester siano sicuri e migliaia di utenti in genere si assicureranno che non ci sia un bug fondamentale nelle basi.
  • Perché i tuoi test non sono idempotenti? Crea un modo per scrivere dati non di produzione o scrivere su un altro endpoint. Scegli alcuni schemi di denominazione prevedibili. Pre-caricare il database del server di riposo prima dei test. E ci sono probabilmente altri modi per farlo, il metodo è davvero tattico e dovrebbe dipendere dalla natura dell'app.
risposta data 09.03.2013 - 03:27
fonte
2

Penso che simulare le risposte del server REST sia il modo migliore per testare il client.

Per Ruby, c'è la gemma di FakeWeb che puoi utilizzare per emettere risposte false - link .

Inoltre, in JavaScript puoi usare qualcosa come Sinon.JS, che ti dà un server falso - link .

    
risposta data 09.03.2013 - 03:03
fonte
1

Come altri hanno già detto, se stai testando un client non devi andare fino alla creazione, all'eliminazione, ecc. sul server. Un sacco di volte non hai nemmeno bisogno di prendere in giro un server. Devi solo assicurarti di fare le richieste giuste e gestire correttamente le risposte, sia che sia scritto in Ruby, Python, PHP o qualsiasi altra cosa, a un certo punto il tuo client probabilmente userà un metodo da una libreria HTTP per fare una richiesta ed è sufficiente prendere in giro quel metodo, verificare come viene chiamato e restituire un risultato del test.

Prendi un ipotetico client Python che usa urllib2 per fare richieste. Probabilmente hai qualche metodo nel client, chiamiamolo get() , che ha una chiamata a urllib2.Request() in esso. Hai solo bisogno di prendere in giro la chiamata al get() della tua classe.

@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 un'ipotetica classe your.Client con un metodo get_object() che genera l'url corretto per ottenere qualcosa da qualche API. Per fare la richiesta il client chiama il suo metodo get() 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 mocked restituisce la risposta JSON configurata ( your_mock.return_value ) che il client deve gestire e si farebbero ulteriori asserzioni per verificare che gestisse i dati previsti nel modo previsto.

    
risposta data 09.03.2013 - 04:42
fonte
1

Quello che descrivi è uno scenario di test di integrazione. Di solito sono un po 'scomodi da configurare e abbattere. Li rende lenti a correre e abbastanza spesso fragili.

L'approccio con le fixture è altrettanto imbarazzante e goffo, ma è il modo predefinito in cui alcuni framework lo fanno, ad es. Rails, ed è già supportato. Hanno bisogno del caso di test astratto o qualcosa di simile per preparare il database con i dispositivi. (Attenzione ai nomi insoliti delle categorie di test in Rails, i test unitari con fixture DB sono in senso stretto anche test di integrazione.)

Il modo in cui farei lo scenario è accettare il controllo specifico del test sullo stato dell'applicazione API o sul suo database. È possibile avere endpoint aggiuntivi per l'installazione e la rimozione, che sono presenti solo nell'ambiente di test. In alternativa, puoi parlare con il database (o qualsiasi altra cosa tu stia utilizzando) dietro il retro della tua applicazione / API.

Se ritieni che questo sia troppo (nel senso di inutile) sforzo, allora considera che l'approccio con le fixture per i database fa proprio questo: usare mezzi addizionali specifici per test per manipolare il database o lo stato dell'applicazione.

Non penso che questa discussione abbia a che fare con la natura stateless di HTTP, comunque. HTTP è senza stato, ma nella maggior parte dei casi l'applicazione non è assolutamente valida. Sembra un po 'come te dove cerchi la rigidità REST. Potresti anche avere tutte le risorse completamente creabili, leggibili e cancellabili. In tal caso, puoi semplicemente eseguire tutte le operazioni di configurazione e rimozione tramite i normali mezzi API. Questo spesso non viene fatto nella pratica, però, perché non vuoi includere alcune operazioni da una comprensione aziendale della tua applicazione, almeno non al di fuori dell'ambiente di test.

    
risposta data 09.03.2013 - 11:19
fonte
1

Monkey Patch

Al mio lavoro eseguiamo l'ATDD utilizzando un framework xUnit e scimmia che rattoppano le chiamate di rete tra il client e il server. Nello stesso spazio del processo viene caricato il client, patch della scimmia la chiamata di rete nella parte superiore del codice dello stack del server REST. Tutte le chiamate vengono quindi emesse dal client come se fossero normalmente e il codice del server ottiene esattamente le richieste che normalmente apparirebbero.

Vantaggi

  • non è necessario sincronizzarsi con l'avvio del server (perché non c'è un server)
  • usa il classico setup delle unità e il metodo di smontaggio per gestire cose come i fixture
  • possibilità 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, mancanza 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.)
risposta data 09.03.2013 - 05:45
fonte

Leggi altre domande sui tag