I test di unità ti consentono di verificare la qualità del codice dopo averlo scritto, ma puoi anche utilizzarli per migliorare il processo di sviluppo man mano che procedi. Anziché scrivere i test dopo aver completato lo sviluppo dell'applicazione, ti consigliamo di scriverli man mano. In questo modo, puoi progettare unità di codice piccole, manutenibili e riutilizzabili. Inoltre, ti consente di testare il codice in modo accurato e rapido.
Quando esegui test di unità locale, esegui test che rimangono all'interno del tuo ambiente di sviluppo senza coinvolgere componenti remoti. App Engine fornisce utilità di test che utilizzano implementazioni locali di Datastore e di altri servizi App Engine. Ciò significa che puoi verificare l'utilizzo di questi servizi da parte del tuo codice localmente, senza eseguire il deployment del codice in App Engine, utilizzando gli stub di servizio.
Uno stub di servizio è un metodo che simula il comportamento del servizio. Ad esempio, lo stub del servizio Datastore mostrato in Scrivere test di Datastore e Memcache ti consente di testare il codice di Datastore senza effettuare richieste al Datastore reale. Qualsiasi entità archiviata durante un test di unità del datastore viene memorizzata in memoria, non nel datastore, ed eliminata al termine dell'esecuzione del test. Puoi eseguire test piccoli e rapidi senza alcuna dipendenza dal datastore stesso.
Questo documento fornisce alcune informazioni sulla configurazione di un framework di test, poi descrive come scrivere test di unità su diversi servizi App Engine locali.
Configurazione di un framework di test
Anche se le utilità di test dell'SDK non sono legate a un framework specifico, in questa guida viene utilizzato JUnit per gli esempi, in modo da avere un riferimento concreto e completo da cui partire. Prima di iniziare a scrivere i test, dovrai aggiungere il JAR JUnit 4 appropriato al classpath di test. A questo punto, puoi scrivere un test JUnit molto semplice.
Se utilizzi Eclipse, seleziona il file sorgente del test da eseguire. Seleziona il menu Esegui > Esegui come > Test JUnit. I risultati del test vengono visualizzati nella finestra della console.
Introduzione alle utilità di test Java 8
MyFirstTest
mostra la configurazione di test più semplice possibile e, per i test che non hanno dipendenze dalle API App Engine o dalle implementazioni dei servizi locali, potrebbe non essere necessario altro. Tuttavia, se i test o il codice in test hanno queste dipendenze, aggiungi i seguenti file JAR al percorso di classe di test:
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
Questi file JAR rendono disponibili per i test le API di runtime e le implementazioni locali di queste API.
I servizi App Engine si aspettano una serie di elementi dal loro ambiente di esecuzione e la configurazione di questi elementi richiede una buona quantità di codice boilerplate. Anziché configurarlo autonomamente, puoi utilizzare le utilità nel pacchetto com.google.appengine.tools.development.testing
. Per utilizzare questo pacchetto,
aggiungi il seguente file JAR al percorso di classe di test:
${SDK_ROOT}/lib/testing/appengine-testing.jar
Dai un'occhiata alla documentazione Javadoc del pacchetto com.google.appengine.tools.development.testing
. La classe più importante di questo pacchetto è LocalServiceTestHelper, che gestisce tutta la configurazione dell'ambiente necessaria e fornisce un punto di configurazione di primo livello per tutti i servizi locali a cui potresti voler accedere nei tuoi test.
Per scrivere un test che accede a un servizio locale specifico:
- Crea un'istanza di
LocalServiceTestHelper
con un'implementazione diLocalServiceTestConfig
per quel servizio locale specifico. - Chiama
setUp()
sulla tua istanzaLocalServiceTestHelper
prima di ogni test etearDown()
dopo ogni test.
Scrittura di test di Datastore e memcache
L'esempio seguente testa l'utilizzo del servizio datastore.
In questo esempio, LocalServiceTestHelper
configura e rimuove le parti dell'ambiente di esecuzione comuni a tutti i servizi locali e LocalDatastoreServiceTestConfig
configura e rimuove le parti dell'ambiente di esecuzione specifiche per il servizio del datastore locale. Se
leggi la documentazione Javadoc
scoprirai che questo comporta la configurazione del servizio del datastore locale per mantenere
tutti i dati in memoria (anziché eseguire lo svuotamento sul disco a intervalli regolari) e
eliminare tutti i dati in memoria al termine di ogni test. Questo è solo il comportamento predefinito per un test del datastore e, se non è quello che vuoi, puoi modificarlo.
Modifica dell'esempio per accedere a memcache anziché a Datastore
Per creare un test che acceda al servizio memcache locale, puoi utilizzare il codice riportato sopra, con alcune piccole modifiche.
Invece di importare le classi relative a Datastore, importa quelle relative a
memcache. Devi ancora importare LocalServiceTestHelper
.
Modifica il nome della classe che stai creando e l'istanza di LocalServiceTestHelper
in modo che siano specifici per Memcache.
Infine, modifica il modo in cui esegui il test in modo che sia pertinente a Memcache.
Come nell'esempio del datastore, LocalServiceTestHelper
e LocalServiceTestConfig
specifico per il servizio (in questo caso
LocalMemcacheServiceTestConfig
) gestiscono l'ambiente di esecuzione.
Scrittura di test di Cloud Datastore
Se la tua app utilizza Cloud Datastore, ti consigliamo di scrivere test che verifichino il comportamento dell'applicazione in caso di eventuale coerenza. LocalDatastoreServiceTestConfig
offre opzioni che semplificano questa procedura:
Impostando la percentuale di job non applicati su 100, diamo al datastore locale l'istruzione di operare con la massima coerenza finale. La coerenza finale massima indica che le scritture verranno confermate, ma non verranno mai applicate, pertanto le query globali (non antecedenti) non riusciranno mai a rilevare le modifiche. Naturalmente, questo non è rappresentativo della quantità di coerenza finale che la tua applicazione vedrà durante l'esecuzione in produzione, ma a scopo di test è molto utile poter configurare il datastore locale in modo che si comporti sempre in questo modo.
Se vuoi un controllo più granulare sulle transazioni che non vengono applicate, puoi registrare il tuo HighRepJobPolicy
:
Le API di test sono utili per verificare che l'applicazione si comporti correttamente in presenza di coerenza finale, ma tieni presente che il modello locale di coerenza di lettura con replica elevata è un'approssimazione del modello di coerenza di lettura con replica elevata di produzione, non una replica esatta. Nell'ambiente locale, l'esecuzione di un get()
di un Entity
che appartiene a un gruppo di entità con una scrittura non applicata rende sempre visibili i risultati della scrittura non applicata alle query globali successive. In produzione non è così.
Scrivere test delle coda di attività
I test che utilizzano la coda di attività locale sono un po' più complessi perché, a differenza di datastore e memcache, l'API coda di attività non espone un'utilità per esaminare lo stato del servizio. Dobbiamo accedere alla coda di attività locale stessa per verificare che un'attività sia stata pianificata con i parametri previsti. Per farlo, abbiamo bisogno di com.google.appengine.api.taskqueue.dev.LocalTaskQueue
.
Tieni presente che chiediamo a LocalTaskqueueTestConfig
un handle per l'istanza del servizio locale, quindi esaminiamo il servizio locale stesso per assicurarci che l'attività sia stata pianificata come previsto. Tutte le implementazioni di LocalServiceTestConfig
espongono un metodo simile. Potresti non averne sempre bisogno, ma prima o poi sarai felice che sia lì.
Impostazione del file di configurazione queue.xml
Le librerie di test della coda di attività consentono di specificare un numero qualsiasi di configurazioni queue.xml
su base LocalServiceTestHelper tramite il metodo LocalTaskQueueTestConfig.setQueueXmlPath
. Al momento, qualsiasi impostazione del limite di frequenza della coda viene ignorata dal server di sviluppo locale.
Non è possibile eseguire contemporaneamente più attività contemporaneamente a livello locale.
Ad esempio, un progetto potrebbe dover eseguire test in base al
queue.xml
file che verrà caricato e utilizzato dall'applicazione App Engine. Supponendo che il file queue.xml
si trovi nella posizione standard, il codice campione riportato sopra potrebbe essere modificato come segue per concedere all'accesso di test alle code specificate nel file src/main/webapp/WEB-INF/queue.xml
:
Modifica il percorso del file queue.xml
in base alla struttura del file del progetto.
Utilizza il metodo QueueFactory.getQueue
per accedere alle code per nome:
Scrivere test delle attività differite
Se il codice dell'applicazione utilizza le attività differite, le utilità di test Java semplificano la scrittura di un test di integrazione che verifica i risultati di queste attività.
Come nel primo esempio di coda di attività locale, utilizziamo un
LocalTaskqueueTestConfig
, ma questa volta lo inizializziamo con alcuni
argomenti aggiuntivi che ci offrono un modo semplice per verificare non solo che la tâche
è stata pianificata, ma anche che è stata eseguita: chiamiamo
setDisableAutoTaskExecution(false)
per indicare alla coda di attività locale di
eseguire automaticamente le attività. Chiamiamo
setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
per indicare
alla coda di attività locale di utilizzare un callback che sappia come eseguire le attività posticipate. Infine, chiamiamo setTaskExecutionLatch(latch)
per indicare alla coda delle attività locali di decrementare il latch dopo ogni esecuzione dell'attività. Questa configurazione
ci consente di scrivere un test in cui inseriamo in coda un'attività differita, aspettiamo che l'attività venga eseguita e poi verifichiamo che si sia comportata come previsto durante l'esecuzione.
Scrivere test delle funzionalità dei servizi locali
I test delle funzionalità comportano la modifica dello stato di alcuni servizi, come datastore, blobstore, memcache e così via, ed eseguono l'applicazione su quel servizio per determinare se risponde come previsto in condizioni diverse. Lo stato della funzionalità può essere modificato utilizzando la classe LocalCapabilitiesServiceTestConfig.
Il seguente snippet di codice imposta lo stato della funzionalità del servizio Datastore su disattivato e poi esegue un test sul servizio. Se necessario, puoi sostituire il datastore con altri servizi.
Il test di esempio crea prima un oggetto Capability
inizializzato in datastore, poi un oggetto CapabilityStatus
impostato su DISATTIVATO. LocalCapabilitiesServiceTestConfig
viene creato con la funzionalità e lo stato impostati utilizzando gli oggetti Capability
e CapabilityStatus
appena creati.
LocalServiceHelper
viene poi creato utilizzando l'oggetto
LocalCapabilitiesServiceTestConfig
. Ora che il test è stato configurato, viene creato il DatastoreService
e viene inviata una query per determinare se il test genera i risultati previsti, in questo caso un CapabilityDisabledException
.
Scrittura di test per altri servizi
Sono disponibili utilità di test per Blobstore e altri servizi App Engine. Per un elenco di tutti i servizi con implementazioni locali per i test, consulta la documentazione di LocalServiceTestConfig
.
Scrivere test con aspettative di autenticazione
Questo esempio mostra come scrivere test che verificano la logica che utilizza UserService per determinare se un utente ha eseguito l'accesso o dispone dei privilegi di amministratore. Tieni presente che qualsiasi utente con il ruolo di base Visualizzatore, Editor o Proprietario o il ruolo predefinito Amministratore app App Engine ha privilegi amministrativi.
In questo esempio, stiamo configurando LocalServiceTestHelper
con LocalUserServiceTestConfig
per poter utilizzare UserService
nel nostro test, ma stiamo anche configurando alcuni dati di ambiente relativi all'autenticazione sul LocalServiceTestHelper
stesso.
In questo esempio, stiamo configurando LocalServiceTestHelper
con LocalUserServiceTestConfig
per poter utilizzare OAuthService
.