Come scrivere test su un servizio alla fine coerente?

17

Sto creando un servizio su Datastore di Google App Engine, che è un archivio dati coerente alla fine. Per la mia applicazione, va bene.

Tuttavia, sto sviluppando test che fanno cose come PUT object e quindi GET oggetto e controllo delle proprietà sull'oggetto restituito. Sfortunatamente, poiché il datastore è alla fine coerente, questi semplici test non sono riproducibili.

Come testare un servizio alla fine coerente?

    
posta Doug Richardson 19.08.2015 - 02:53
fonte

8 risposte

15

Considerare i requisiti non funzionali quando si progettano i test funzionali - se il proprio servizio ha un requisito non funzionale di "Consistente in x (secondi / minuti / ecc.)", eseguire semplicemente le richieste PUT, attendere x, quindi eseguire il Richiedi richieste.

A quel punto, se i dati non sono ancora arrivati, puoi considerare la richiesta PUT non conforme ai tuoi requisiti.

    
risposta data 02.11.2016 - 21:29
fonte
7

Vuoi davvero che i tuoi test siano veloci e coerenti. Se inizi a creare test che a volte possono fallire a causa della coerenza, ignorerai il test quando fallisce, e allora a che cosa serve?

Crea un servizio falso che gestisce le richieste PUT e GET, ma ha un'operazione aggiuntiva per renderlo coerente. Il tuo test è quindi:

datastore.do_put(myobj);
datastore.make_consistent();
validate(datastore.do_get(), myobj);

Ciò consente di verificare il comportamento del software quando GET recupera correttamente l'oggetto PUT. Consente inoltre di testare il comportamento del software quando GET non trova l'oggetto (o l'oggetto corretto) a causa del fatto che il servizio non è ancora coerente. Lascia la chiamata a make_consistent() .

Vale comunque la pena di test che interagiscono con il servizio reale, ma dovrebbero essere eseguiti al di fuori del normale flusso di lavoro di sviluppo, poiché non saranno mai affidabili al 100% (ad esempio se il servizio non è attivo). Questi test dovrebbero essere usati per:

  1. fornisce metriche in media e nel caso peggiore tra un PUT e un successivo GET che diventa coerente; e
  2. verifica che il tuo servizio falso si comporti in modo simile al servizio reale. Vedi link
risposta data 03.11.2016 - 08:31
fonte
6
  • Dopo PUT, ritenta GET N volte fino al successo. Fallisce se non ha successo dopo N tentativi.
  • Dormi tra PUT e GET

Sfortunatamente, devi scegliere i valori magici (N o durata del sonno) per entrambe queste tecniche.

    
risposta data 19.08.2015 - 02:53
fonte
4

OK, quindi. "Cosa stai provando" è la domanda chiave.

  • Sto testando la mia logica interna di ciò che accade supponendo che la roba di Google funzioni

In questo caso dovresti prendere in giro i servizi di google e restituire sempre una risposta.

  • Sto testando che la mia logica può far fronte agli errori transitori che so che Google produrrà

In questo caso dovresti prendere in giro i servizi di google e restituire sempre l'errore transitorio prima della risposta corretta

  • Sto verificando che il mio prodotto funzionerà effettivamente con il vero servizio Google

Dovresti iniettare i veri servizi di google ed eseguire il test. Ma! Il codice che si sta testando deve contenere la gestione degli errori transitori (retry). Quindi dovresti ottenere una risposta coerente. (a meno che Google non si comporti molto male)

    
risposta data 03.11.2016 - 11:14
fonte
2

A quanto ho capito, il datastore di Google Cloud consente sia query consistenti che alla fine coerenti .

Il compromesso è che le query strongmente coerenti sono piuttosto limitate in termini di velocità (qualcosa con cui puoi convivere durante i test).

Una possibilità potrebbe essere quella di mettere le query sull'archivio dati all'interno di un wrapper che può consentire una consistenza strong a scopo di test.

Ad esempio, potresti avere metodi chiamati start_debug_strong_consistency() e end_debug_strong_consistency() .

Il metodo start creerebbe una chiave che può essere utilizzata come chiave dell'antenato per tutte le query successive e il metodo end eliminerebbe la chiave.

L'unica modifica alle query effettive che stai provando sarebbe chiamare setAncestor(your_debug_key) se tale chiave esiste.

    
risposta data 03.11.2016 - 10:45
fonte
1

Un approccio, che in teoria è bello ma potrebbe non essere sempre pratico, è quello di rendere tutte le operazioni di scrittura nel sistema sotto test idempotente . Ciò significa che, supponendo che il tuo test test testa le cose in un ordine sequenziale fisso, puoi riprovare tutte le letture e tutte le scritture singolarmente fino a ottenere il risultato che ti aspetti, riprovando fino a qualche timeout che definisci nel il codice di prova viene superato. Cioè, fai la cosa A1, riprova se necessario fino a quando il risultato è B1, quindi fai la cosa A2, riprova se necessario fino a quando il risultato è B2, e così via.

Quindi non devi preoccuparti di controllare le precondizioni delle operazioni di scrittura, perché le operazioni di scrittura le controlleranno già per te, e le riprovi solo finché non ci riescono!

Utilizza gli stessi timeout "predefiniti" il più possibile, che possono essere aumentati se l'intero sistema diventa più lento e sostituisce i valori predefiniti singolarmente quando ritentano operazioni particolarmente lente.

    
risposta data 02.11.2016 - 21:24
fonte
1

Un servizio come Datastore di Google App Engine si basa sulla replica dei dati tra diversi punti di presenza (POP) distribuiti a livello globale. Qualsiasi test di integrazione per un servizio alla fine coerente è in realtà un test della velocità di replica di quel servizio attraverso il suo insieme di POP. La velocità con cui il contenuto viene distribuito a tutti i POP in un dato servizio non sarà la stessa per tutti i POP all'interno del servizio, a seconda di una serie di fattori, come il metodo di replica e vari problemi di trasporto su Internet - questi sono due esempi che rappresentano la maggior parte dei report in qualsiasi servizio di archivio dati eventualmente coerente (almeno questa era la mia esperienza mentre stavo lavorando per un CDN importante).

Per testare efficacemente la replica di un oggetto su una determinata piattaforma, è necessario impostare il test per richiedere lo stesso oggetto inserito di recente da ciascuno dei POP del servizio. Sto suggerendo di testare l'elenco dei POP da una a cinque volte o fino a quando tutti i POP nei tuoi POP elencano i report che hanno l'oggetto. Ecco una serie di intervalli in cui eseguire il test che sei libero di regolare: 1, 5, 60 minuti, 12 ore, 25 ore dopo averlo posizionato sul datastore. La chiave sta registrando i risultati a ogni intervallo per una revisione e un'analisi successive al fine di dare un'idea della capacità di un dato servizio di replicare globalmente gli oggetti. Spesso i servizi di datastore richiamano una copia locale su un POP una volta che è stata richiesta localmente [l'instradamento viene eseguito tramite il protocollo BGP, motivo per cui il test deve richiedere l'oggetto da ogni specifico POP affinché sia globalmente valido per una determinata piattaforma] . Nel caso di Datastore di Google, dovresti impostare il test per interrogare un determinato oggetto da "oltre 70 punti di presenza in 33 paesi"; probabilmente dovresti ottenere l'URL dell'elenco di indirizzi specifici di POP dall'assistenza Google [ref: link ] o se Google è utilizzando Fastly per la replica, supporta rapidamente [ link ].

Un paio di vantaggi di questo metodo: 1) Avrai un'idea della piattaforma di replica di un dato servizio, conoscerai i suoi limiti e punti deboli nel suo complesso su scala globale [come avveniva durante il test di integrazione]. 2) Per qualunque oggetto tu collaudi avrai uno strumento disponibile per riscaldare i contenuti [fai quella prima richiesta che crea la copia su un dato POP locale] - così ti fornirai un modo per assicurarti che il contenuto sia distribuito globalmente prima che i tuoi clienti lo richiedano ovunque sulla terra.

    
risposta data 03.11.2016 - 05:12
fonte
0

Ho esperienza con Google App Engine Datastore. Funzionando localmente, sorprendentemente, spesso è più "alla fine" che "coerente". L'esempio più semplice: crea una nuova entità e poi recuperala. Spesso negli ultimi 5 anni ho visto che l'SDK in esecuzione locale non trova immediatamente la nuova entità, ma la trova dopo circa mezzo secondo.

Tuttavia, correndo contro i veri server di Google, non ho visto questo comportamento. Cercano di far sì che il tuo client Datastore venga sempre eseguito sullo stesso server, quindi di solito le modifiche sono immediatamente riflesse nelle query.

Il mio consiglio per i test di integrazione è di eseguirli contro i veri server, e quindi probabilmente non avrai bisogno di mettere polling o ritardi falsi per ottenere i risultati.

    
risposta data 07.11.2016 - 00:17
fonte