2012-07-31 9 views
10

sto cercando di sviluppare alcuni test di integrazione efficaci per il mio applicazione GAE/j. Ho familiarità con https://developers.google.com/appengine/docs/java/tools/localunittesting - questi strumenti sono ottimi per i test di piccole unità. Ora sono interessato allo sviluppo di test di integrazione che testino le richieste web attuali. Ad esempio, vorrei testare che web.xml stia mappando servlet e filtri agli URL previsti e test che i miei JSP generino ciò che mi aspetto.test di integrazione per Google App Engine (java)

Il mio obiettivo era quello di portare un server di sviluppo locale all'interno della JVM, che ho potuto sparare contro le richieste. Sono aperto ad altre strategie di integrazione, però; come ho detto sopra, voglio solo testare efficacemente la generazione JSP e altre funzionalità a livello di richiesta.

Sono riuscito a utilizzare DevAppServerFactory per avviare un server di sviluppo nella stessa JVM. Tuttavia, sembra che il DevAppServer che questo genera utilizza un classloader separato dalla JVM principale. Ciò rende il test molto più impegnativo: non posso utilizzare nessuna delle classi Local * TestConfig locali per controllare il comportamento di questo server. Allo stesso modo, non posso "rollare i miei" ganci per modificare il comportamento tramite ad es. statica, dal momento che la statica che posso mutare nel cablaggio di test non è la stessa statica a cui sta guardando DevAppServer. Ciò rende difficile saltare le funzionalità non centrali per il test corrente (ad esempio richiedendo il login), per iniettare errori, iniettare mock, ecc. Questo limita davvero il modo in cui posso testare il mio codice in modo completo ed efficiente.

ho trovato una vera e propria carenza di documentazione sul web per test di integrazione con App Engine. Sono sicuro che qualcuno l'ha già fatto prima ... ci sono consigli o risorse che puoi condividere?

risposta

2

Fondamentalmente, è necessario fare due cose:

  1. Aggiungi due servlet (o altro), che deve essere attivata solo durante i test, che consentono di richiamare setup e teardown sul aiutante in remoto.
  2. Fai il tuo motore di servlet servire richieste in un completamente modo single-threaded. Questo è necessario perché, per qualche motivo, la classe Helper di Google fornisce ha effetto solo nel thread corrente.
0

sono d'accordo questo problema è scarsamente documentata.
Sono riuscito a scrivere un test end-to-end che avvia un server come una scatola nera e invia le richieste HTTP.

funziona così:

package com.project.org; 

import static org.junit.Assert.assertEquals; 
import java.io.File; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.List; 
import org.junit.After; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import com.google.appengine.api.urlfetch.HTTPResponse; 
import com.google.appengine.api.urlfetch.URLFetchServiceFactory; 
import com.google.appengine.tools.development.testing.BaseDevAppServerTestConfig; 
import com.google.appengine.tools.development.testing.DevAppServerTest; 
import com.google.appengine.tools.development.testing.DevAppServerTestRunner; 
import com.google.appengine.tools.development.testing.LocalServiceTestHelper; 

@RunWith(DevAppServerTestRunner.class) 
@DevAppServerTest(HelloWorldTest.TestConfig.class) 
public class HelloWorldTest { 

    public class TestConfig extends BaseDevAppServerTestConfig { 

    @Override public File getSdkRoot() { 
     // You may need to tweak this. 
     return new File("../../appengine-java-sdk-1.9.15"); 
    } 

    @Override public File getAppDir() { 
     return new File("war"); 
    } 

    @Override 
    public List<URL> getClasspath() { 
     // There may be an easier way to do this. 
     List<URL> classPath = new ArrayList<>(); 
     try { 
     String separator = System.getProperty("path.separator"); 
     String[] pathElements = System.getProperty("java.class.path").split(separator); 
     for (String pathElement : pathElements) { 
      classPath.add(new File(pathElement).toURI().toURL()); 
     } 
     } 
     catch (MalformedURLException e) { 
     throw new RuntimeException(e); 
     } 
     return classPath; 
    } 
    } 

    private final LocalServiceTestHelper testHelper; 
    private final String port; 

    public HelloWorldTest() { 
    testHelper = new LocalServiceTestHelper(); 
    port = System.getProperty("appengine.devappserver.test.port"); 
    } 

    @Before public void setUpServer() { 
    testHelper.setUp(); 
    } 

    @After public void tearDown() { 
    testHelper.tearDown(); 
    } 

    @Test public void testHelloWorld() throws Exception { 
    URL url = new URL("http://localhost:" + port + "/hello"); 
    HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(url); 
    assertEquals(200, response.getResponseCode()); 
    assertEquals("Hello world!", new String(response.getContent(), "UTF-8")); 
    } 
} 

Ora il problema che ho è che se si dispone di due di questi test, con ogni passando singolarmente, non è possibile eseguirli nello stesso binario. L'eccezione sulla linea 37 del this file avere buttato:

IllegalStateException ("Dev appserver è già in esecuzione.")

Non sei sicuro di come risolvere questo problema.