2013-02-12 11 views
17

Ho diversi bean nel mio contesto Spring che hanno lo stato, quindi mi piacerebbe ripristinare quello stato prima/dopo i test unitari.Come posso ottenere un elenco di bean istanziati da Spring?

La mia idea era quella di aggiungere un metodo a una classe helper che attraversa tutti i bean nel contesto Spring, controlla i metodi annotati con @Before o @After e li invoca.

Come si ottiene un elenco di di fagioli istanziati da ApplicationContext?

Nota: Le soluzioni che semplicemente operare su tutti i bean definiti sono inutili perché ho molti chicchi di pigri e alcuni di loro non devono essere istanziati perché sarebbe fallire per alcuni test (ad esempio ho un fagioli che hanno bisogno di un java.sql.DataSource ma i test lavoro perché non hanno bisogno di quel fagiolo).

+1

forse definire "Dopo" pointcut http://static.springsource.org/spring/docs/3.0.x/reference/aop.html –

+0

L'annotazione @DirtiesContext nel test dell'unità non fa ciò che vuoi? –

+0

@NicolasMommaerts: No. Il ripristino di alcuni bean sarebbe molto più economico rispetto alla creazione di tutto il contesto da zero. –

risposta

21

Ad esempio:

public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) { 
      List<Object> singletons = new ArrayList<Object>(); 

      String[] all = ctx.getBeanDefinitionNames(); 

      ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory(); 
      for (String name : all) { 
       Object s = clbf.getSingleton(name); 
       if (s != null) 
        singletons.add(s); 
      } 

      return singletons; 

    } 
+0

'getSingleton()' funziona perfettamente per me. Aggiornato il mio senso (vedi la mia risposta) –

+0

@AaronDigulla Sto lavorando con 'Spring-WebMVC'. Come posso accedere a 'WebApplicationContext' per il mio' DispatcherServlet'? Senza di esso, non posso ripetere i fagioli instanciati. – smwikipedia

+1

@AaronDigulla L'ho risolto. Posso usare l'interfaccia 'ApplicationContextAware'. Rif: http://stackoverflow.com/questions/9602664/print-all-the-spring-beans-that-are-loaded – smwikipedia

3

Non sono sicuro che questo possa esserti d'aiuto o meno.

È necessario creare la propria annotazione es. MyAnnot. E posiziona quell'annotazione sulla classe che vuoi ottenere. E quindi usando il seguente codice si potrebbe ottenere il bean istanziato.

ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); 
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class)); 
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){ 
    System.out.println(beanDefinition.getBeanClassName()); 
} 

In questo modo è possibile ottenere tutti i bean con annotazioni personalizzate.

0

Ho creato un gist ApplicationContextAwareTestBase.

classe

questo helper fa due cose:

  1. imposta tutti i campi interni a null. Ciò consente a Java di liberare memoria che non viene più utilizzata. È meno utile con Spring (il contesto Spring conserva comunque i riferimenti a tutti i bean).

  2. Prova a trovare tutti i metodi annotati con @After in tutti i bean nel contesto e li richiama dopo il test.

In questo modo, è possibile ripristinare facilmente lo stato dei propri singleton/mock senza dover distruggere/aggiornare il contesto.

Esempio: Si dispone di un DAO finto:

public void MockDao implements IDao { 

    private Map<Long, Foo> database = Maps.newHashMap(); 

    @Override 
    public Foo byId(Long id) { return database.get(id)); 

    @Override 
    public void save(Foo foo) { database.put(foo.getId(), foo); } 

    @After 
    public void reset() { database.clear(); } 
} 

L'annotazione farà in modo reset() saranno chiamati dopo ogni unit test per ripulire lo stato interno.

3

ho dovuto migliorare un po '

@Resource 
AbstractApplicationContext context; 

@After 
public void cleanup() { 
    resetAllMocks(); 
} 

private void resetAllMocks() { 
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 
    for (String name : context.getBeanDefinitionNames()) { 
     Object bean = beanFactory.getSingleton(name); 
     if (Mockito.mockingDetails(bean).isMock()) { 
      Mockito.reset(bean); 
     } 
    } 
} 
0

Utilizzando le risposte precedenti, ho aggiornato questa opzione per utilizzare Java 8 Streams API:

@Inject 
private ApplicationContext applicationContext; 

@Before 
public void resetMocks() { 
    ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory(); 
    Stream.of(applicationContext.getBeanDefinitionNames()) 
      .map(n -> beanFactory.getSingleton(n)) 
      // My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say 
      // .map(ConfigurableListableBeanFactory::getSingleton) 
      .filter(b -> Mockito.mockingDetails(b).isMock()) 
      .forEach(Mockito::reset); 
} 
Problemi correlati