2015-06-08 10 views
9

Diciamo che sto scrivendo i test di integrazione Spring per un servizio REST A. Questo servizio a sua volta colpisce un altro servizio REST B e ottiene un elenco di URI da colpire su REST servizio C. È una specie di modello di auto-scoperta. Voglio prendere in giro le risposte B e C usando MockRestServiceServer.
Ora la risposta da B è un elenco di URI, sono tutti molto simili, e per il bene di questo esempio consente di dire la mia risposta da B è in questo modo:Spring MockRestServiceServer che gestisce più richieste allo stesso URI (rilevamento automatico)

{ 
    uris: ["/stuff/1.json", "/stuff/2.json", "/stuff/39.json", "/stuff/47.json"] 
} 

Semplicemente servizio à aggiungerà ciascuno di loro sull'URL di base per il servizio C e fanno quelle richieste.
Mocking B è facile poiché è solo una richiesta.
Mocking C è una seccatura come avrei dovuto prendere in giro ogni singolo URI per appropriarsi della risposta simulata. Voglio automatizzarlo!
Quindi, prima ho scrivere il mio matcher a non corrisponde un URL completo, ma parte di esso:

public class RequestContainsUriMatcher implements RequestMatcher { 
    private final String uri; 

    public RequestContainsUriMatcher(String uri){ 
     this.uri = uri; 
    } 

    @Override 
    public void match(ClientHttpRequest clientHttpRequest) throws IOException, AssertionError { 
     assertTrue(clientHttpRequest.getURI().contains(uri)); 
    } 
} 

Questo funziona bene come ora posso fare questo:

public RequestMatcher requestContainsUri(String uri) { 
    return new RequestContainsUriMatcher(uri); 
} 

MockRestServiceServer.createServer(restTemplate) 
      .expect(requestContainsUri("/stuff")) 
      .andExpect(method(HttpMethod.GET)) 
      .andRespond(/* I will get to response creator */); 

Ora ho solo bisogno di un creatore risposta che conosce l'URL richiesta completa e dove i dati finto siede (avrò come file JSON nella cartella risorse di test):

public class AutoDiscoveryCannedDataResponseCreator implements ResponseCreator { 
    private final Function<String, String> cannedDataBuilder; 

    public AutoDiscoveryCannedDataResponseCreator(Function<String, String> cannedDataBuilder) { 
     this.cannedDataBuilder = cannedDataBuilder; 
    } 

    @Override 
    public ClientHttpResponse createResponse(ClientHttpRequest clientHttpRequest) throws IOException { 
     return withSuccess(cannedDataBuilder.apply(requestUri), MediaType.APPLICATION_JSON) 
        .createResponse(clientHttpRequest); 
    } 
} 

Ora roba è facile, ho HAV e per scrivere un costruttore che prende l'URI di richiesta come una stringa e restituisce dati falsi, come una stringa! Brillante!

public ResponseCreator withAutoDetectedCannedData() { 
    Function<String, String> cannedDataBuilder = new Function<String, String>() { 
     @Override 
     public String apply(String requestUri) { 
      //logic to get the canned data based on URI 
      return cannedData; 
     } 
    }; 

    return new AutoDiscoveryCannedDataResponseCreator(cannedDataBuilder); 
} 

MockRestServiceServer.createServer(restTemplate) 
      .expect(requestContainsUri("/stuff")) 
      .andExpect(method(HttpMethod.GET)) 
      .andRespond(withAutoDetectedCannedData()); 

funziona benissimo! .... Per la prima richiesta.
Dopo la prima richiesta (/stuff/1.json) il mio MockRestServiceServer risponde con il messaggio "Errore asserzione: non sono previste ulteriori richieste".
Fondamentalmente, posso fare tutte le richieste a quel MockRestServiceServer quante erano le chiamate .expect() su di esso. E dal momento che ne avevo solo 1, solo la prima richiesta passerà.
C'è un modo per aggirarlo? Davvero non voglio prendere in giro il servizio C 10 o 20 volte ...

+0

Grazie per l'attuazione RequestContainsUriMatcher – Silentbang

risposta

14

Se si esamina la classe MockRestServiceServer, supporta due metodi 'expect()'. I primi default 'ExpectedCount.once()', ma il secondo metodo permette di modificare questo valore

public ResponseActions expect(RequestMatcher matcher) { 
    return this.expect(ExpectedCount.once(), matcher); 
} 

public ResponseActions expect(ExpectedCount count, RequestMatcher matcher) { 
    return this.expectationManager.expectRequest(count, matcher); 
} 

Ho trovato questo biglietto MockRestServiceServer should allow for an expectation to occur multiple times che delinea alcune opzioni per il secondo metodo.

Nel tuo caso credo che l'aggiunta di import statici e utilizzando i manytimes() il metodo è il codice più ordinato rispetto al ciclo for

ckRestServiceServer 
      .expect(manyTimes(), requestContainsUri("/stuff")) 
      .andExpect(method(HttpMethod.GET)) 

Altre opzioni sono

once(); 
manyTimes(); 
times(5); 
min(2); 
max(8); 
between(3,6); 
+2

Implementato nella primavera 4.3 – aurelije

+1

Questa è ora la risposta corretta e deve essere selezionata come tale. –

8

MODIFICA: vedere la risposta di @emeraldjava che mostra la soluzione corretta per gli utenti di Spring 4.3+.

Purtroppo non c'è alcun piacevole meccanismo per aspettarsi più chiamate. Tu o farlo manualmente o utilizzare i cicli, ad es .:

for (int i = 0; i < 10; i++) {   
     mockRestServiceServer 
       .expect(requestContainsUri("/stuff")) 
       .andExpect(method(HttpMethod.GET)) 
       .andRespond(withAutoDetectedCannedData()); 
} 

Essere consapevoli che le richieste devono essere chiamate senza interruzioni, per esempio non può esserci un'altra chiamata REST che non corrisponde all'URI "/ stuff".

+0

Il ciclo lo risolve Grazie – Silentbang

+0

Una volta verificata l'aspettativa non è possibile aggiungere ulteriori aspettative. 'Impossibile aggiungere ulteriori richieste con test già in corso' –

Problemi correlati