2015-04-09 13 views
12

Ich habe einen Spring Web-Server, der auf eine Anfrage einen externen Aufruf an einige Web-API von Drittanbietern (z. B. retreive Facebook oauth Token). Nach dem Abrufen von Daten aus nennt dies berechnet sie eine Antwort:Mock externen Server während der Integration Tests mit 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; 
    } 
} 

ich einen Integrationstest zu schreiben, dass url prüft schlagen auf meinem Server zu einem gewissen erwarteten Ergebnis führt. Ich möchte jedoch den externen Server vortäuschen, so dass ich nicht einmal einen Internetzugang benötige, um das alles zu testen. Was ist der beste Weg, dies zu tun?

Ich bin ein Neuling im Frühjahr, das ist was ich bisher habe.

@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(); 
    } 
} 

Die tatsächliche reale aufgebaut ist komplizierter - Ich mache Login Facebook oauth und alles, was Logik ist in der Steuerung nicht aber in verschiedenen Objekten Spring Security. Ich vermute jedoch, dass der Testcode derselbe sein sollte, da ich nur URLs triff und eine Antwort erwarte, oder?

Antwort

4

Nach einem wenig mit verschiedenen Szenarien zu spielen, hier ist der einzige Weg, wie kann man das erreichen, was mit minimalen Eingriffen in den Haupt-Code

  1. Refaktorieren Controller einen Parameter für fremde Server-Adresse zu verwenden, wurde gefragt:

    @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' gleich zu 'graph.facebook.com' in application.properties in den src/main/resources

  1. Erstellen Sie einen neuen Controller im Ordner src/test/java, der den Third-Party-Server anspricht.

  2. Überschreiben Sie 'api_host' zum Testen auf 'localhost'. Hier

ist der Code für die Schritte 2 und 3 in einer Datei der Kürze:

@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 
    } 
} 

Gibt es eine schönere Art und Weise, dies zu tun? Alle Rückmeldungen werden geschätzt!

P.S.Hier sind einige Komplikationen ich angetroffenen setzen diese Antwort in realistischer App:

  1. Eclipse-mischt Test und Hauptkonfiguration in Classpath, so dass Sie Ihre Hauptkonfiguration von Testklassen und Parameter könnten vermasseln: https://issuetracker.springsource.com/browse/STS-3882 Verwenden gradle bootRun, es zu meiden

  2. Sie müssen Zugriff auf Ihre mocked Links in der Sicherheitskonfiguration öffnen, wenn Sie Federsicherheit eingerichtet haben. Anhängen zu einer Sicherheitskonfigurations stattdessen mit einer Hauptkonfiguration Konfiguration von Messing:

    @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. Es ist nicht einfach https Links in Integrationstests zu schlagen. Ich benutze schließlich TestRestTemplate mit einer benutzerdefinierten Anforderungsfactory und konfigurierte SSLConnectionSocketFactory.

0

Sie könnten eine andere Spring-Konfigurationsdatei haben, die den gleichen Endpunkt wie die HelloController-Klasse zur Verfügung stellt. Sie könnten dann einfach die gespeicherte JSON-Antwort zurückgeben.

Von Ihrem Code bin ich mir nicht sicher, was Sie gerade erreichen wollen. Wenn Sie einfach sehen möchten, dass der Anruf bei Facebook funktioniert, dann gibt es keinen Ersatz für Tests mit dem Dienst, der tatsächlich mit Facebook kommuniziert. Die Facebook-Antwort zu verspotten, nur um sicherzustellen, dass sie richtig verspottet wird, erscheint mir nicht als ein schrecklich nützlicher Test.

Wenn Sie testen, dass die Daten, die von Facebook zurückkommen, in irgendeiner Weise mutiert sind und Sie sicherstellen möchten, dass die Arbeit daran korrekt ist, dann könnten Sie dies in einer separaten Methode tun nahm die Facebook-Antwort als Parameter und führte dann die Mutation durch. Sie könnten dann basierend auf verschiedenen json-Eingaben überprüfen, dass es richtig funktioniert.

Sie könnten testen, ohne den Webservice überhaupt einzubringen.

+0

Ich kam mit diesem Code, um zu verstehen, wie man einen komplizierteren Integrationstest schreibt. Das eigentliche System, das ich testen und dann umgestalten möchte, besteht aus mehreren Spring-Sicherheitsmodulen, die zusammenarbeiten, um oauth2 mit facebook bereitzustellen. All dies geschieht mit gesicherter zustandsloser REST-Architektur, die eigene Macken hat. Letztendlich möchte ich dieses komplexe System testen - es gibt mehrere Schritte vor Facebook-Aufruf und einige danach. Diese Controller-Frage ist der einfachste, klare erste Schritt, den ich mir jetzt vorstellen kann, und wenn ich das ganze System posten werde, ist es völlig unmöglich zu antworten. – otognan

+0

Ist es fair zu sagen, dass Sie das Verhalten Ihrer App teilweise testen möchten, wenn die Spring Security Authentifizierungsfehler aufweist? –

+0

Ich habe schon einen funktionierenden Prototyp, aber es ist hässlich. Ich habe keinen Druck auf dieses Projekt - also möchte ich ein paar Tests schreiben, die sicherstellen, dass das System gut funktioniert, während ich ein großes Refactoring machen werde. Also ja, teilweise möchte ich Testauthentifizierungsverweigerungen und andere Fehlermodi, aber hauptsächlich möchte ich sicher sein, dass ich die funktionierende Funktionalität beim Refactoring bewahre. – otognan

Verwandte Themen