2015-04-09 11 views
12

Possiedo un server Web Spring che in una richiesta effettua una chiamata esterna a un'API Web di terze parti (ad esempio, richiede token Facebook oauth). Dopo aver ottenuto i dati dal presente invito si calcola una risposta:Mock server esterno durante i test di integrazione con Spring

@RestController 
public class HelloController { 
    @RequestMapping("/hello_to_facebook") 
    public String hello_to_facebook() { 
     // Ask facebook about something 
     HttpGet httpget = new HttpGet(buildURI("https", "graph.facebook.com", "/oauth/access_token")); 
     String response = httpClient.execute(httpget).getEntity().toString(); 
     // .. Do something with a response 
     return response; 
    } 
} 

sto scrivendo un test di integrazione che controlla che colpisce url sul mio server porta a qualche risultato atteso. Tuttavia, voglio prendere in giro il server esterno in locale in modo che non abbia nemmeno bisogno dell'accesso a Internet per testare tutto questo. Qual è il modo migliore per farlo?

Sono un novizio in primavera, questo è quello che ho finora.

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = Application.class) 
@WebAppConfiguration 
@IntegrationTest({}) 
public class TestHelloControllerIT {   
    @Test 
    public void getHelloToFacebook() throws Exception { 
     String url = new URL("http://localhost:8080/hello_to_facebook").toString(); 
     //Somehow setup facebook server mock ... 
     //FaceBookServerMock facebookMock = ... 

     RestTemplate template = new TestRestTemplate(); 
     ResponseEntity<String> response = template.getForEntity(url, String.class); 
     assertThat(response.getBody(), equalTo("...")); 

     //Assert that facebook mock got called 
     //facebookMock.verify(); 
    } 
} 

Il vero effettivo allestimento è più complicato - Sto facendo Facebook OAuth login e tutto ciò che la logica non è nel controller, ma in vari oggetti Primavera di sicurezza. Tuttavia, sospetto che il codice di test debba essere lo stesso poiché sto solo scrivendo url e aspetto una risposta, non è vero?

risposta

4

Dopo aver giocato un po 'con vari scenari, qui è l'unico modo in cui si può ottenere ciò che è stato chiesto con interventi minimi per il codice principale

  1. refactoring del controller per utilizzare un parametro per l'indirizzo del server di terze parti:

    @RestController 
    public class HelloController { 
        @Value("${api_host}") 
        private String apiHost; 
    
        @RequestMapping("/hello_to_facebook") 
        public String hello_to_facebook() { 
         // Ask facebook about something 
         HttpGet httpget = new HttpGet(buildURI("http", this.apiHost, "/oauth/access_token")); 
         String response = httpClient.execute(httpget).getEntity().toString(); 
         // .. Do something with a response 
         return response + "_PROCESSED"; 
        } 
    } 
    

'api_host' è uguale a 'graph.facebook.com' in application.properties nelle src/main/risorse

  1. Creare un nuovo controller nella cartella src/test/java che deride il server di terze parti.

  2. Sostituire 'api_host' per il test su 'localhost'.

Ecco il codice per i passaggi 2 e 3 in un unico file per brevità:

@RestController 
class FacebookMockController { 
    @RequestMapping("/oauth/access_token") 
    public String oauthToken() { 
     return "TEST_TOKEN"; 
    } 
} 

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = Application.class) 
@WebAppConfiguration 
@IntegrationTest({"api_host=localhost",}) 
public class TestHelloControllerIT {   
    @Test 
    public void getHelloToFacebook() throws Exception { 
     String url = new URL("http://localhost:8080/hello_to_facebook").toString(); 
     RestTemplate template = new TestRestTemplate(); 
     ResponseEntity<String> response = template.getForEntity(url, String.class); 
     assertThat(response.getBody(), equalTo("TEST_TOKEN_PROCESSED")); 

     // Assert that facebook mock got called: 
     // for example add flag to mock, get the mock bean, check the flag 
    } 
} 

C'è un modo migliore per fare questo? Tutto il feedback è apprezzato!

P.S.Qui sono alcune complicazioni che ho incontrato mettendo questa risposta in applicazione più realistico:

  1. Eclipse mescola di test e configurazione principale nella classpath per cui si potrebbe rovinare la vostra configurazione principale per classi di test e parametri: https://issuetracker.springsource.com/browse/STS-3882 Usa Gradle bootRun per evitarlo

  2. Devi aprire l'accesso ai tuoi link falsificati nella configurazione di sicurezza se hai impostato la sicurezza di primavera. Per aggiungere a una configurazione di sicurezza, invece di fare scherzi con un config di configurazione principale:

    @Configuration 
    @Order(1) 
    class TestWebSecurityConfig extends WebSecurityConfig { 
        @Override 
        protected void configure(HttpSecurity http) throws Exception { 
         http 
          .authorizeRequests() 
           .antMatchers("/oauth/access_token").permitAll(); 
         super.configure(http); 
        } 
    } 
    
  3. Non è semplice per colpire i collegamenti https in test di integrazione. Finisco per utilizzare TestRestTemplate con fabbrica di richieste personalizzate e configurato SSLConnectionSocketFactory.

0

Si potrebbe avere un altro file di configurazione molla che espone lo stesso endpoint della classe HelloController. Potresti quindi semplicemente restituire la risposta in scatola di JSON.

Dal tuo codice, non sono sicuro di quello che stai cercando di realizzare. Se vuoi semplicemente vedere che la chiamata su Facebook funziona, allora non c'è alcun sostituto per il test contro il servizio che in realtà parla con Facebook. Prendere in giro la risposta di Facebook solo per assicurarsi che sia deriso correttamente, non mi sembra un test terribilmente utile.

Se si sta verificando per verificare che i dati restituiti da Facebook vengano in qualche modo modificati e si desidera assicurarsi che il lavoro svolto su di esso sia corretto, è possibile eseguire tale operazione in un metodo separato che ha preso la risposta di Facebook come parametro, e poi ha effettuato la mutazione. È quindi possibile verificare in base a vari ingressi JSON che funzionava correttamente.

È possibile testare senza coinvolgere il servizio Web.

+0

Sono arrivato con questo codice per capire come scrivere un test di integrazione più complicato. Il sistema attuale che voglio testare e poi refactoring è composto da diversi moduli di sicurezza Spring che funzionano in accordo per fornire oauth2 con facebook. Tutto ciò che viene fatto con un'architettura REST stateless protetta che ha le sue peculiarità. In definitiva voglio testare questo complesso sistema - ci sono diversi passaggi prima di chiamare Facebook e diversi dopo. Questa domanda del controller è il primo passo più semplice e chiaro a cui riesco a pensare e postare l'intero sistema renderà totalmente impossibile rispondere. – otognan

+0

È corretto dire che in parte vuoi testare il comportamento della tua app quando la sicurezza di primavera ha errori di autenticazione? –

+0

Ho già un prototipo funzionante ma è brutto. Non ho alcuna pressione su questo progetto, quindi quello che voglio fare è scrivere alcuni test che garantiscano il buon funzionamento del sistema mentre eseguirò un grande refactoring. Quindi sì, in parte voglio negare l'autenticazione dei test e altre modalità di errore, ma soprattutto voglio essere sicuro di conservare la funzionalità di lavoro durante il refactoring. – otognan

Problemi correlati