2016-03-30 14 views
5

Ho eseguito i problemi @ComponentScan con le classi @Configuration per i test, ovvero lo @ComponentScan sta eseguendo il test non previsto @Configuration durante i test di integrazione.Esclusa configurazione nelle classi di test da @ComponentScan

Per esempio, diciamo che hai una certa configurazione globale in src/main/java che tira nei componenti all'interno com.example.service, com.example.config.GlobalConfiguration:

package com.example.config; 
... 
@Configuration 
@ComponentScan(basePackageClasses = ServiceA.class) 
public class GlobalConfiguration { 
    ... 
} 

E 'destinato a tirare in due servizi, com.example.services.ServiceA e com.example.services.ServiceB, annotata con @Component e @Profile("!test") (omesso per brevità).

Poi in src/test/java, com.example.services.ServiceATest:

package com.example.services; 
... 
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = ServiceATest.ServiceATestConfiguration.class) 
public class ServiceATest { 
    ... 
    @Configuration 
    public static class ServiceATestConfiguration { 
     @Bean 
     public ServiceA serviceA() { 
      return ServiceA(somemocking...); 
     } 
    } 
} 

E anche com.example.ServiceBIntegrationTest, che deve tirare in GlobalConfiguration.class per essere un test di integrazione, ma evita ancora tirando in implementazioni pericolose @ActiveProfiles("test"):

package com.example.services; 
... 
@RunWith(SpringJUnit4ClassRunner.class) 
@ActiveProfiles("test") 
@ContextConfiguration(classes = {GlobalConfiguration.class, ServiceBIntegrationTest.ServiceBIntegrationTestConfiguration.class}) 
public class ServiceBIntegrationTest { 
    ... 
    @Configuration 
    public static class ServiceBIntegrationTestConfiguration { 
     @Bean 
     public ServiceB serviceB() { 
      return ServiceB(somemocking...); 
     } 
    } 
} 

l'evidente intenzione del ServiceBIntegrationTest è tirare nella configurazione completa applicazione tramite src/main/javaGlobalConfiguration, escludere pericoloso componenti tramite @ActiveProfiles("test") e sostituire quei componenti esclusi con le proprie implementazioni. Tuttavia, durante i test vengono combinati lo spazio dei nomi di src/main/java e src/test/java, pertanto di trova più nel classpath di quanto normalmente farebbe, ovvero il bean ServiceA definito in ServiceA.ServiceATestConfiguration. Ciò potrebbe facilmente portare a conflitti e risultati non intenzionali.

Ora, si potrebbe fare qualcosa su GlobalConfiguration come @ComponentScan(..., excludeFilters= @ComponentScan.Filter(type = FilterType.REGEX, pattern = "\\.*(T|t)est\\.*")), ma questo ha problemi a sé stanti. Affidarsi alle convenzioni sui nomi è piuttosto fragile; tuttavia, anche se si è annullata un'annotazione @TestConfiguration e si utilizza FilterType.ANNOTATION, si sta effettivamente rendendo il vostro src/main/java consapevole del proprio src/test/java, che non dovrebbe essere, IMO (vedere nota di seguito).

Così com'è, ho risolto il problema utilizzando un profilo aggiuntivo. Su ServiceA, aggiungo un nome profilo univoco, in modo che l'annotazione del profilo diventi qualcosa come @ActiveProfiles("test,serviceatest"). Quindi, su ServiceATest.ServiceATestConfiguration aggiungo l'annotazione @Profile("serviceatest"). Ciò limita in modo efficace la portata del ServiceATestConfiguration con relativamente poco in testa, ma sembra che sia:

a) Sto usando @ComponentScan in modo non corretto, o

b) Ci dovrebbe essere un modello molto più pulito per la gestione di questo problema

Quale è?


nota: sì, l'applicazione è di test-aware perché sta usando @Profile("!test"), ma direi che presenta la domanda un po 'di test-aware per la difesa contro l'utilizzo delle risorse improprio e rendendolo di test-aware per garantire la correttezza dei test sono cose molto diverse.

risposta

1

Vedo che stai cercando di simulare i bean Spring durante il test di integrazione.Se si combinano l'annotazione @Profile e @ActiveProfiles con l'annotazione @Primary, la maggior parte dei mal di testa dovrebbe scomparire e non è necessario contrassegnare i bean di produzione con @Profile("!test").

Ho scritto un blog post on the topic con Github examples.

Reazione al commento:

Con struttura del pacchetto. Scansione componente analizza tutti i pacchetti all'interno del pacchetto e dei sottoprogetti correnti. Se non si desidera analizzare i bean, è sufficiente modificare la struttura del pacchetto in modo che il bean non si trovi sotto l'ombrello di scansione dei componenti.

Spring non differenzia i pacchetti da src/test/java o src/main/java. Cercare di escludere i bean di produzione con @Profile("!test") è l'odore del design. Dovresti evitarlo. Suggerirei di dare una possibilità di approccio dal blog citato.

Si noti che quando si esegue l'override del bean con l'annotazione @Primary, potrebbe essere necessario utilizzare l'annotazione @DirtiesContext per avere un foglio pulito per altri test.

+1

Sì, ci sono alcuni vantaggi a tale modello, ma non è quello che riguarda la mia domanda. Sto chiedendo come limitare al meglio l'ambito della configurazione all'interno di un pacchetto scansionato dal componente. – jwilner

+1

La modifica della confezione sarebbe in conflitto con l'ambito locale del pacchetto, che è ovviamente importante per il test. Questo è insoddisfacente. – jwilner

Problemi correlati