java - tutorial - Poblando Spring @Value durante la prueba unitaria
test junit netbeans (5)
Intento escribir una prueba unitaria para un bean simple que se usa en mi programa para validar formularios. El bean está anotado con @Component
y tiene una variable de clase que se inicializa usando @Value("${this.property.value}") private String thisProperty;
Me gustaría escribir pruebas unitarias para los métodos de validación dentro de esta clase; sin embargo, si es posible, me gustaría hacerlo sin utilizar el archivo de propiedades. Mi razonamiento detrás de esto, es que si el valor que estoy extrayendo del archivo de propiedades cambia, me gustaría que eso no afecte mi caso de prueba. Mi caso de prueba es probar el código que valida el valor, no el valor en sí mismo.
¿Hay alguna forma de usar código Java dentro de mi clase de prueba para inicializar una clase Java y rellenar la propiedad Spring @Value dentro de esa clase y luego usar eso para probar?
Encontré este cómo que parece estar cerca, pero todavía usa un archivo de propiedades. Prefiero que todo sea código Java.
Gracias
Agregar PropertyPlaceholderConfigurer en la configuración me funciona.
@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;
}
}
Y en clase de prueba
@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());
}
}
Desde Spring 4.1, puede configurar los valores de las propiedades solo en código utilizando la anotación org.springframework.test.context.TestPropertySource
en el nivel de clase Pruebas unitarias. Puede usar este enfoque incluso para inyectar propiedades en instancias de beans dependientes
Por ejemplo
@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: es necesario tener una instancia de org.springframework.context.support.PropertySourcesPlaceholderConfigurer
en el contexto Spring
Editar 24-08-2017: Si usa SpringBoot 1.4.0 y posterior, puede inicializar las pruebas con las anotaciones @SpringBootTest
y @SpringBootConfiguration
. Más información here
En caso de SpringBoot tenemos el siguiente código
@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);
}
}
Esto parece funcionar, aunque sigue siendo un poco detallado (me gustaría algo más corto aún):
@BeforeClass
public static void beforeClass() {
System.setProperty("some.property", "<value>");
}
// Optionally:
@AfterClass
public static void afterClass() {
System.clearProperty("some.property");
}
Si es posible, trataría de escribir esas pruebas sin Spring Context. Si creas esta clase en tu prueba sin resorte, entonces tienes control total sobre sus campos.
Para establecer el campo @value
puede usar Springs ReflectionTestUtils
; tiene un método setField
para establecer campos privados.
@ver JavaDoc : ReflectionTestUtils.setField (java.lang.Object, java.lang.String, java.lang.Object)
Si lo desea, puede ejecutar las pruebas en Spring Context y establecer las propiedades requeridas dentro de la clase de configuración de Spring. Si usa JUnit, use SpringJUnit4ClassRunner y defina la clase de configuración dedicada para sus pruebas de esa manera:
La clase bajo prueba:
@Component
public SomeClass {
@Autowired
private SomeDependency someDependency;
@Value("${someProperty}")
private String someProperty;
}
La clase de prueba:
@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() { ... }
}
Y la clase de configuración para esta prueba:
@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);
}
}
Habiendo dicho eso, no recomendaría este enfoque, solo lo agregué aquí para referencia. En mi opinión, una forma mucho mejor es usar Mockito runner. En ese caso, no ejecuta pruebas dentro de Spring en absoluto, lo cual es mucho más claro y simple.