2013-06-27 21 views
105

Sto provando a scrivere un Unit Test per un bean semplice che viene utilizzato nel mio programma per convalidare i moduli. Il fagiolo è annotato con @Component e ha una variabile di classe che viene inizializzata utilizzando @Value("${this.property.value}") private String thisProperty;Population Spring @Value durante Unit Test

Vorrei scrivere unit test per i metodi di validazione all'interno di questa classe, tuttavia, se possibile, mi piacerebbe farlo senza utilizzare il file delle proprietà . Il mio ragionamento dietro questo, è che se il valore che sto tirando dal file delle proprietà cambia, vorrei che ciò non influisse sul mio caso di test. Il mio test case sta testando il codice che convalida il valore, non il valore stesso.

C'è un modo per utilizzare il codice Java all'interno della mia classe di test per inizializzare una classe Java e popolare la proprietà Spring @Value all'interno di quella classe, quindi usarla per testarla?

Ho trovato questo How To che sembra essere vicino, ma utilizza ancora un file di proprietà. Preferirei che fosse tutto codice Java.

Grazie

risposta

88

Se possibile vorrei provare a scrivere quelle test senza Contesto Primavera. Se crei questa classe nel test senza primavera, hai il pieno controllo sui suoi campi.

Per impostare il campo @value è possibile utilizzare le molle ReflectionTestUtils - ha un metodo setField per impostare campi privati.

@see JavaDoc: ReflectionTestUtils.setField(java.lang.Object, java.lang.String, java.lang.Object)

+1

Esattamente quello che stavo cercando di fare e ciò che stavo cercando di impostare il valore all'interno della mia classe, grazie! – Kyle

+1

O anche senza dipendenze Spring cambiando il campo in accesso predefinito (protetto da pacchetto) per renderlo facilmente accessibile al test. –

+2

Esempio: 'org.springframework.test.util.ReflectionTestUtils.setField (classUnderTest," field "," value ");' – Olivier

36

Se si desidera, è comunque possibile eseguire i test all'interno del contesto della molla e impostare le proprietà necessarie all'interno classe di configurazione di primavera. Se si utilizza JUnit, utilizzare SpringJUnit4ClassRunner e definire classe di configurazione dedicato per i test del genere:

La classe in prova:

@Component 
public SomeClass { 

    @Autowired 
    private SomeDependency someDependency; 

    @Value("${someProperty}") 
    private String someProperty; 
} 

La classe di test:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = SomeClassTestsConfig.class) 
public class SomeClassTests { 

    @Autowired 
    private SomeClass someClass; 

    @Autowired 
    private SomeDependency someDependency; 

    @Before 
    public void setup() { 
     Mockito.reset(someDependency); 

    @Test 
    public void someTest() { ... } 
} 

E la classe di configurazione per questo test:

@Configuration 
public class SomeClassTestsConfig { 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer properties() throws Exception { 
     final PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); 
     Properties properties = new Properties(); 

     properties.setProperty("someProperty", "testValue"); 

     pspc.setProperties(properties); 
     return pspc; 
    } 
    @Bean 
    public SomeClass getSomeClass() { 
     return new SomeClass(); 
    } 

    @Bean 
    public SomeDependency getSomeDependency() { 
     // Mockito used here for mocking dependency 
     return Mockito.mock(SomeDependency.class); 
    } 
} 

Detto questo, non lo consiglio questo approccio, l'ho appena aggiunto qui per riferimento. Secondo me, il modo migliore è usare Mockito runner. In tal caso non esegui test all'interno di Spring, il che è molto più chiaro e semplice.

+2

Sono d'accordo che la maggior parte della logica dovrebbe essere testata con Mockito. Vorrei che esistesse un modo migliore per testare la presenza e la correttezza delle annotazioni rispetto all'esecuzione di test attraverso Spring. – Altair7852

15

Questo sembra funzionare, anche se ancora un po 'prolisso (mi piacerebbe qualcosa di più breve ancora):

@BeforeClass 
public static void beforeClass() { 
    System.setProperty("some.property", "<value>"); 
} 

// Optionally: 
@AfterClass 
public static void afterClass() { 
    System.clearProperty("some.property"); 
} 
+0

Penso che questa risposta sia più pulita in quanto è agnostica di Spring, funziona bene per diversi scenari, come quando devi usare i test runner personalizzati e non puoi semplicemente aggiungere l'annotazione '@ TestProperty'. – raspacorp

4

Aggiunta PropertyPlaceholderConfigurer in configurazione sta lavorando per me.

@Configuration 
@ComponentScan 
@EnableJpaRepositories 
@EnableTransactionManagement 
public class TestConfiguration { 
@Bean 
public DataSource dataSource() { 
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 
    builder.setType(EmbeddedDatabaseType.DERBY); 
    return builder.build(); 
} 

@Bean 
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 
    entityManagerFactoryBean.setDataSource(dataSource()); 
    entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.model" }); 
    // Use hibernate 
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter); 
    entityManagerFactoryBean.setJpaProperties(getHibernateProperties()); 
    return entityManagerFactoryBean; 
} 

private Properties getHibernateProperties() { 
    Properties properties = new Properties(); 
    properties.put("hibernate.show_sql", "false"); 
    properties.put("hibernate.dialect", "org.hibernate.dialect.DerbyDialect"); 
    properties.put("hibernate.hbm2ddl.auto", "update"); 
    return properties; 
} 

@Bean 
public JpaTransactionManager transactionManager() { 
    JpaTransactionManager transactionManager = new JpaTransactionManager(); 
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); 
    return transactionManager; 
} 

@Bean 
PropertyPlaceholderConfigurer propConfig() { 
    PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer(); 
    placeholderConfigurer.setLocation(new ClassPathResource("application_test.properties")); 
    return placeholderConfigurer; 
} 

}

E nella classe di test

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = TestConfiguration.class) 
public class DataServiceTest { 

@Autowired 
private DataService dataService; 

@Autowired 
private DataRepository dataRepository; 

@Value("${Api.url}") 
private String baseUrl; 

@Test 
public void testUpdateData() { 
    List<Data> datas = (List<Data>) dataRepository.findAll(); 
    assertTrue(datas.isEmpty()); 
    dataService.updateDatas(); 
    datas = (List<Data>) dataRepository.findAll(); 
    assertFalse(datas.isEmpty()); 
} 

}

57

Dalla primavera del 4.1 è possibile impostare valori di proprietà solo in codice utilizzando l'annotazione org.springframework.test.context.TestPropertySource sul livello di classe Unità Test. È possibile utilizzare questo approccio anche per l'iniezione di proprietà in istanze bean dipendenti

Per esempio

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = FooTest.Config.class) 
@TestPropertySource(properties = { 
    "some.bar.value=testValue", 
}) 
public class FooTest { 

    @Value("${some.bar.value}") 
    String bar; 

    @Test 
    public void testValueSetup() { 
    assertEquals("testValue", bar); 
    } 


    @Configuration 
    static class Config { 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer propertiesResolver() { 
     return new PropertySourcesPlaceholderConfigurer(); 
    } 

    } 

} 

Nota: E 'necessario avere istanza di org.springframework.context.support.PropertySourcesPlaceholderConfigurer nel contesto primavera

Modifica 24-08- 2017: Se si utilizza SpringBoot 1.4.0 e versioni successive, è possibile inizializzare i test con le annotazioni @SpringBootTest e @SpringBootConfiguration. Maggiori informazioni here

In caso di SpringBoot abbiamo seguente codice

@SpringBootTest 
@SpringBootConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
@TestPropertySource(properties = { 
    "some.bar.value=testValue", 
}) 
public class FooTest { 

    @Value("${some.bar.value}") 
    String bar; 

    @Test 
    public void testValueSetup() { 
    assertEquals("testValue", bar); 
    } 

} 
+0

Grazie, finalmente qualcuno ha risposto a come sovrascrivere Valore e non come impostare un campo. Io ricavo i valori dal campo stringa in PostConstruct e quindi ho bisogno che il valore stringa venga impostato da Spring, non dopo la costruzione. – tequilacat

+0

Sono felice di aiutare –