2016-06-09 6 views
6

Zrobiłem sporo wyszukiwania w Internecie i nie mogę znaleźć przykładu testów jednostkowych z autowyred konstruktorem. Używam Springa do autoprzesyłania wartości z pliku właściwości do mojej aplikacji. Chcę przetestować metodę startową MyApp.java, ale mam autowyreduktora, więc nie wiem, jak utworzyć instancję MyApp. Bez autowired właściwości, robiłam to w moim badanej jednostki:Jak mogę JUnit przetestować konstruktor autiosered Spring?

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

ja nie chcę mock autowiring, jak trzeba uzyskać wartości z pliku właściwości oraz dodatkowo komplikować, jestem konfigurowanie wszystkiego za pomocą adnotacji. Nie mam pliku spring.xml, application-context.xml ani web.xml. Jak więc zrobić prezentację/testować metodę uruchamiania MyApp? Próbowałem dodać w @RunWith (SpringJUnit4ClassRunner.class) i autowiring MyApp myApp, ale generuje błędy dotyczące nie załadowania kontekstu aplikacji, które nie są ustalone przez implementację ApplicationContextAware w klasie testowej.

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

Tutaj jest złożyć moje app.properties

# Spring Configuration for My application 

#properties for the embedded jetty server 
http_server_port=12345 

Oto 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; 
    } 
} 

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

UPDATE: Oto moja klasa konfiguracja

@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

Dodanie tylko '@ RunWith' i bez informowania, która konfiguracja do załadowania zakończy się niepowodzeniem. Musisz dodać "public static class" do swojej klasy testowej, wstawiając '@ Configuration' i' @ComponentScan ("your.package") '. Na wyższym poziomie sugerowałbym użycie Spring Boot również do bootstrapu aplikacji i wstrzykiwania/używania właściwości, ponieważ wydaje się, że robisz dokładnie to, co Spring Boot zapewnia natychmiast po uruchomieniu (włączając w to wsparcie testowe dla niego). –

+0

@ M.Deinum Niestety, Spring Boot nie jest rozwiązaniem dla mnie z powodu konfliktów z innymi narzędziami. Żeby wyjaśnić, czy mówisz, że powinienem zmienić swoją klasę testową na publiczną klasę statyczną MyAppTest, czy powinienem zrobić kolejną klasę? Kiedy próbuję utworzyć stateczność klasy MyAppTest, pojawia się komunikat o błędzie z informacją, że jest to niedozwolony modyfikator. – jencoston

+0

Nie trzeba dodawać wewnętrznej klasy konfiguracji.Nie widzę też żadnej różnicy w tym, co Spring Boot robi ze śmiechem z wewnętrznego serwera, tak jak ty sam, tak naprawdę nie powinno być żadnej różnicy. –

Odpowiedz

6

Możemy makiety obiektów za pomocą ramy jmockito.

Korzystanie @InjectMocks do wstrzykiwania zależności poprzez Mockito Masz również @InjectMocks adnotacji który stara się zrobić konstruktor, metody lub pola iniekcji zależność w zależności od typu. Poniższy kod jest lekko zmodyfikowanym przykładem z Javadoc.

Następująca będzie klasa testu jednostkowego.

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

}

Upewnij się, że używasz @RunWith (MockitoJUnitRunner.class) Więcej informacji można znaleźć http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html.

+0

Dziękuję za odpowiedź! Jeśli kpię z konstruktora MyApp, czy nadal będę uzyskiwał wartości z pliku właściwości? Metoda, którą próbuję przetestować, uruchamia wbudowany serwer pomostowy i musi uzyskać wartości z pliku właściwości, aby to zrobić. – jencoston

+0

używanie @InjectMocks jest uważane za złą praktykę. zobacz na przykład: https://lkrnac.net/blog/2014/02/promoting-constructor-field-injection/ – JonyD

+0

O komentarzu @JonyD: Przeczytałem kilka komentarzy i większość z nich przeczy temu artykułowi. Czy znalazłeś jakąś ostateczną odpowiedź? – matthieusb