2016-06-09 18 views
6

Ho fatto un bel po 'di ricerche online e non riesco a trovare un esempio di test unitario con un costruttore autowired. Sto usando Spring per autowire nei valori da un file delle proprietà alla mia applicazione. Voglio testare unitamente il metodo di avvio di MyApp.java, ma ho un costruttore autowired quindi non so come istanziare MyApp. Senza le proprietà autowired, stavo facendo questo nel mio test di unità:Come posso JUnit testare un costruttore autowired di Spring?

@Test 
public void testStart() { 
    try{ 
    MyApp myApp = new MyApp(); 
    myApp.start(); 
    } 
    catch (Exception e){ 
    fail("Error thrown") 
    } 
} 

Non voglio prendere in giro l'autowiring, come ho bisogno di ottenere i valori dal file delle proprietà e per complicare ulteriormente le cose, io sono configurazione di tutto tramite annotazioni. Non ho un file spring.xml, application-context.xml o web.xml. Quindi, come faccio a istanziare/testare il metodo di avvio di MyApp? Ho provato ad aggiungere in @RunWith (SpringJUnit4ClassRunner.class) e ad attivare MyApp myApp, ma genera errori sul mancato caricamento del contesto dell'applicazione che non viene risolto implementando ApplicationContextAware nella classe di test.

Ecco MyApp.java

@Component 
public class MyApp { 

    private static ApplicationContext applicationContext; 
    private static MyAppProperties myAppProperties; 

    //Obtain the values from the app.properties file 
    @Autowired 
    MyApp(MyAppProperties myAppProps){ 
     myAppProperties = myAppProps; 
    } 

    public static void main(String[] args) throws Exception { 
    // Instantiate the application context for use by the other classes 
    applicationContext = new AnnotationConfigApplicationContext("com.my.company"); 

    start(); 
    } 

    /** 
    * Start the Jetty server and configure the servlets 
    * 
    * @throws Exception 
    */ 
    public static void start() throws Exception { 
     // Create Embedded Jetty server 
     jettyServer = new Server(); 

     // Configure Jetty so that it stops at JVM shutdown phase 
     jettyServer.setStopAtShutdown(true); 
     jettyServer.setStopTimeout(7_000); 

     // Create a list to hold all of the handlers 
     final HandlerList handlerList = new HandlerList(); 

     // Configure for Http 
     HttpConfiguration http_config = new HttpConfiguration(); 
     http_config.setSecureScheme("https"); 
     http_config.setSecurePort(myAppProperties.getHTTP_SECURE_PORT()); 
    .... 
    } 
} 

Qui è il file miei app.properties

# Spring Configuration for My application 

#properties for the embedded jetty server 
http_server_port=12345 

Ecco MyAppProperties.java

@Component 
public class MyAppProperties implements ApplicationContextAware { 

    private ApplicationContext applicationContext; 

    //List of values from the properties files to be autowired 
    private int HTTP_SERVER_PORT; 
    ... 

    @Autowired 
    public MyAppProperties(@Value("${http_server_port}") int http_server_port, ...){ 
     this.HTTP_SERVER_PORT = http_server_port; 
    } 

    /** 
    * @return the applicationContext 
    */ 
    public ApplicationContext getApplicationContext() { 
     return applicationContext; 
    } 

    /** 
    * @param applicationContext 
    *   the applicationContext to set 
    */ 
    @Override 
    public void setApplicationContext(ApplicationContext applicationContext) { 
     this.applicationContext = applicationContext; 
    } 

    /** 
    * @param name 
    *   the name to set 
    */ 
    public void setHTTP_SERVER_PORT(String name) { 
     JETTY_SERVER_NAME = name; 
    } 

    /** 
    * @return the httpServerPort 
    */ 
    public int getHTTP_SERVER_PORT() { 
     return HTTP_SERVER_PORT; 
    } 
} 

Ecco MyAppTest.java

@RunWith(SpringJUnit4ClassRunner.class) 
public class MyAppTest implements ApplicationContextAware{ 

    private ApplicationContext applicationContext; 

    @Override 
    public void setApplicationContext(ApplicationContext appContext) { 
     applicationContext = appContext;  
    } 

    @Autowired 
    private MyApp myapp; 

    @Test 
    public void testStart(){ 
    try { 
     if(myapp != null){ 
      myapp.start(); 
     } 
     else{ 
      fail("myapp is null"); 
     } 
    } catch (Exception e) { 
     fail("Error thrown"); 
     e.printStackTrace(); 
    } 
    } 
} 

AGGIORNAMENTO: Ecco la mia classe di configurazione

@Configuration 
@Component 
public class ApplicationConfig implements ApplicationContextAware { 

    private final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfig.class); 
    private ApplicationContext applicationContext; 

    /** 
    * @return the applicationContext 
    */ 
    public ApplicationContext getApplicationContext() { 
     LOGGER.debug("Getting Application Context", applicationContext); 
     return applicationContext; 
    } 

    /** 
    * @param applicationContext 
    *   the applicationContext to set 
    */ 
    @Override 
    public void setApplicationContext(ApplicationContext applicationContext) { 
     this.applicationContext = applicationContext; 
    } 

    // Needed for @Value 
    /** 
    * Property sources placeholder configurer. 
    * 
    * @return the property sources placeholder configurer 
    */ 
    @Bean 
    public PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer() { 
     PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer(); 
     propertyPlaceholderConfigurer.setLocation(new ClassPathResource("app.properties")); 
     return propertyPlaceholderConfigurer; 
    } 
    ... 
} 
+0

L'aggiunta di '@ RunWith' e senza indicare quale configurazione caricare non funzionerà. Devi aggiungere una 'public static class' alla tua classe di test mettendo' @ Configuration' e '@ComponentScan (" your.package ")' su di esso. Ad un ulteriore livello suggerirei di utilizzare Spring Boot anche per il bootstrap dell'applicazione e per iniettare/usare le proprietà come sembra che stiano facendo esattamente ciò che Spring Boot fornisce out-of-the-box (incluso il supporto di test per questo). –

+0

@ M.Deinum Sfortunatamente, Spring Boot non è un'opzione per me a causa di conflitti con altri strumenti. Giusto per chiarire, stai dicendo che dovrei cambiare la mia classe di test in public class class MyAppTest o dovrei fare un'altra lezione? Quando provo a rendere statica la classe MyAppTest, viene visualizzato un messaggio di errore che indica che si tratta di un modificatore non valido. – jencoston

+0

No, è necessario aggiungere una classe di configurazione interna.Inoltre non vedo alcuna differenza in ciò che Spring Boot fa con il ridere di un server interno come fai tu stesso, non ci dovrebbe essere alcuna differenza. –

risposta

6

Siamo in grado di prendere in giro gli oggetti utilizzando il framework jmockito.

Uso @InjectMocks per l'iniezione di dipendenza mediante Mockito si ha anche la @InjectMocks annotazione che cerca di fare il costruttore, metodo o campo iniezione di dipendenza in base al tipo. Il seguente codice è un esempio leggermente modificato dal Javadoc.

// Mockito can construct this class via constructor 
public class ArticleManager { 
     ArticleManager(ArticleCalculator calculator, ArticleDatabase database) { 
     } 
} 

// Mockito can also perform method injection 
public class ArticleManager { 
     ArticleManager() { } 
     void setDatabase(ArticleDatabase database) { } 
     void setCalculator(ArticleCalculator calculator) { } 
} 

// Mockito can also perform field injection 
public class ArticleManager { 

    private ArticleDatabase database; 
    private ArticleCalculator calculator; 
} 

Quanto segue sarà la classe di test dell'unità.

@RunWith(MockitoJUnitRunner.class) 
public class ArticleManagerTest { 
    @Mock private ArticleCalculator calculator; 
    @Mock private ArticleDatabase database; 
    @Spy private UserProvider userProvider = new ConsumerUserProvider(); 

    // creates instance of ArticleManager 
    // and performs constructor injection on it 
    @InjectMocks private ArticleManager manager; 

    @Test public void shouldDoSomething() { 
      // assume that ArticleManager has a method called initialize which calls a method 
      // addListener with an instance of ArticleListener 
      manager.initialize(); 

     // validate that addListener was called 
      verify(database).addListener(any(ArticleListener.class)); 
    } 

}

Assicurarsi che si sta utilizzando @RunWith (MockitoJUnitRunner.class) Per ulteriori informazioni vedere http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html.

+0

Grazie per la risposta! Se sto prendendo in giro il costruttore MyApp otterrò ancora i valori dal file delle proprietà? Il metodo che sto provando per testare avvia il server jetty incorporato e ha bisogno di ottenere valori dal file delle proprietà per farlo. – jencoston

+0

utilizzando @InjectMocks è considerato una cattiva pratica. vedere, ad esempio: https://lkrnac.net/blog/2014/02/promoting-constructor-field-injection/ – JonyD

+0

Informazioni su @JonyD commento: Ho letto alcuni commenti e la maggior parte di essi contraddicono l'articolo. Hai trovato una risposta definitiva? – matthieusb

Problemi correlati