2016-03-02 14 views
6

Für meine Spring-Boot-App stelle ich eine RestTemplate durch eine @Configuration-Datei zur Verfügung, damit ich sinnvolle Standardwerte hinzufügen kann (ex Timeouts). Für meine Integrationstests möchte ich das RestTemplate überspielen, da ich keine Verbindung zu externen Diensten herstellen möchte - ich weiß, welche Reaktionen zu erwarten sind. Ich habe versucht, eine andere Implementierung in das Integrationstestpaket zu geben, in der Hoffnung, dass letztere die reale Implementierung außer Kraft setzt, aber die Protokolle überprüfen, es ist umgekehrt: die reale Implementierung überschreibt die Testversion.

Wie kann ich sicherstellen, dass der eine aus der TestConfig verwendet wird?Überbrückende Beans in Integrationstests

Dies ist meine Config-Datei:

@Configuration 
public class RestTemplateProvider { 

    private static final int DEFAULT_SERVICE_TIMEOUT = 5_000; 

    @Bean 
    public RestTemplate restTemplate(){ 
     return new RestTemplate(buildClientConfigurationFactory()); 
    } 

    private ClientHttpRequestFactory buildClientConfigurationFactory() { 
     HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); 
     factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT); 
     factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT); 
     return factory; 
    } 
} 

Integrationstest:

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = TestConfiguration.class) 
@WebAppConfiguration 
@ActiveProfiles("it") 
public abstract class IntegrationTest {} 

TestConfiguration Klasse:

@Configuration 
@Import({Application.class, MockRestTemplateConfiguration.class}) 
public class TestConfiguration {} 

Und schließlich MockRestTemplateConfiguration

@Configuration 
public class MockRestTemplateConfiguration { 

    @Bean 
    public RestTemplate restTemplate() { 
     return Mockito.mock(RestTemplate.class) 
    } 
} 
+0

Die Reihenfolge der Importe ändern, sie werden so analysiert, wie sie gelesen werden, also überschreiben die späteren die vorherigen. –

+0

Versuchte es ... das Gleiche. Ich werde meine Frage aktualisieren, um die Änderungen widerzuspiegeln. – mvlupan

+0

Mögliches Duplikat von [Überschreibe eine autovereinigte Bean in Unit Tests] (https://stackoverflow.com/questions/28605833/overriding-an-autowired-bean-in-unit-tests) – LoganMzz

Antwort

8

finden Seit dem Frühjahr Boot-1.4.x gibt es eine Option @MockBean Anmerkung zu fälschen Spring-Beans zu verwenden.

Reaktion auf Kommentar:

Um im Cache zu halten nicht @DirtiesContext verwenden, sondern verwenden @ContextConfiguration(name = "contextWithFakeBean") und es wird eigenen Kontext schaffen, während es Standard-Kontext im Cache zu halten. Spring behält beide (oder wie viele Kontexte Sie haben) im Cache.

Unser Build ist auf diese Art und Weise, wo die meisten Tests standardmäßig nicht-poluted Config verwenden, aber wir haben 4-5 Tests, die Bohnen fälschen. Der Standardkontext wird gut wiederverwendet.

+0

stimme zu, dass dies in den meisten Fällen der richtige Weg ist. Der Nachteil, den ich bei diesem Ansatz gefunden habe, ist, dass Sie den Vorteil des Context-Caching in Ihrer nächsten Testklasse verlieren. Um unsere Integrationstests so schnell wie möglich durchzuführen, erweitern wir wann immer möglich eine Klasse. – mvlupan

+0

wahr, aber manchmal gibt es keinen anderen Weg. Z.B. wenn Sie externe Dienste fälschen müssen. – luboskrnac

+0

Ich habe meine Antwort mit Context-Caching-Trick aktualisiert. – luboskrnac

6

1. können Sie verwenden @Primary Anmerkung:

@Configuration 
public class MockRestTemplateConfiguration { 

    @Bean 
    @Primary 
    public RestTemplate restTemplate() { 
     return Mockito.mock(RestTemplate.class) 
    } 
} 

BTW, ich schrieb blog post about faking Spring bean

2. Aber ich würde vorschlagen, einen Blick auf Spring RestTemplate testing support zu nehmen. Dies wäre ein einfaches Beispiel: private MockRestServiceServer mockServer;

@Autowired 
    private RestTemplate restTemplate; 

    @Autowired 
    private UsersClient usersClient; 

    @BeforeMethod 
    public void init() { 
    mockServer = MockRestServiceServer.createServer(restTemplate); 
    } 

    @Test 
    public void testSingleGet() throws Exception { 
    // GIVEN 
    int testingIdentifier = 0; 
    mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier)) 
     .andExpect(method(HttpMethod.GET)) 
     .andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON)); 


    // WHEN 
    User user = usersClient.getUser(testingIdentifier); 

    // THEN 
    mockServer.verify(); 
    assertEquals(user.getName(), USER0_NAME); 
    assertEquals(user.getEmail(), USER0_EMAIL); 
    } 

Weitere Beispiele finden Sie in my Github repo here

+0

Ich habe den zweiten Vorschlag ausprobiert und es funktioniert super. Danke für den Hinweis..Ich fühle, dass dies der "richtige" Weg ist. – mvlupan