我正在尝试为我的程序中用于验证表单的简单bean编写单元测试。 bean用@Component
注释,并有一个使用@Value("${this.property.value}") private String thisProperty;
初始化的类变量
我想为这个类中的验证方法编写单元测试,但是,如果可能的话,我想在不使用属性文件的情况下这样做。我的理由是,如果我从属性文件中提取的值发生变化,我希望这不会影响我的测试用例。我的测试用例是测试验证值的代码,而不是值本身。
有没有办法在我的测试类中使用Java代码来初始化Java类并在该类中填充Spring @Value属性然后使用它来测试?
我确实发现这个似乎很接近的How To,但仍然使用属性文件。我宁愿这一切都是Java代码。
谢谢
如果可能,我会尝试在没有Spring Context的情况下编写这些测试。如果在没有弹簧的情况下在测试中创建此类,则可以完全控制其字段。
要设置@value
字段,您可以使用Springs ReflectionTestUtils
- 它有一个方法setField
来设置私有字段。
@see JavaDoc: ReflectionTestUtils.setField(java.lang.Object, java.lang.String, java.lang.Object)
从Spring 4.1开始,您可以通过在Unit Tests类级别使用org.springframework.test.context.TestPropertySource
批注在代码中设置属性值。即使将属性注入到依赖的bean实例中,也可以使用此方法
例如
@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();
}
}
}
注意:在Spring上下文中有必要使用org.springframework.context.support.PropertySourcesPlaceholderConfigurer
实例
编辑24-08-2017:如果您使用的是SpringBoot 1.4.0及更高版本,则可以使用@SpringBootTest
和@SpringBootConfiguration
注释初始化测试。更多信息here
对于SpringBoot,我们有以下代码
@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);
}
}
如果需要,您仍然可以在Spring Context中运行测试,并在Spring配置类中设置所需的属性。如果您使用JUnit,请使用SpringJUnit4ClassRunner并为您的测试定义专用配置类:
被测试的课程:
@Component
public SomeClass {
@Autowired
private SomeDependency someDependency;
@Value("${someProperty}")
private String someProperty;
}
测试类:
@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() { ... }
}
以及此测试的配置类:
@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);
}
}
话虽如此,我不推荐这种方法,我只是在这里添加它以供参考。在我看来,更好的方法是使用Mockito跑步者。在这种情况下,你根本不在Spring内部运行测试,这更加清晰和简单。
这似乎有效,虽然仍然有点冗长(我还想要更短的东西):
@BeforeClass
public static void beforeClass() {
System.setProperty("some.property", "<value>");
}
// Optionally:
@AfterClass
public static void afterClass() {
System.clearProperty("some.property");
}
使您的课程既可以单一也可以整合
为了能够为Spring组件类编写普通单元测试(没有运行弹簧容器)和集成测试,您必须使该类可以使用或不使用Spring。 在不需要的情况下在单元测试中运行容器是一种不好的做法,会降低本地构建的速度:您不希望这样做。 我添加了这个答案,因为这里的答案似乎没有显示出这种区别,所以他们系统地依赖于一个正在运行的容器。
所以我认为你应该将这个属性定义为类的内部:
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
到将由Spring注入的构造函数参数:
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
单元测试示例
你可以在没有Spring的情况下实例化Foo
并为property
注入任何值,这要归功于构造函数:
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
集成测试示例
由于properties
的@SpringBootTest
属性,您可以使用Spring Boot以这种简单的方式在上下文中注入属性:
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
您可以使用替代@TestPropertySource
但它添加了一个额外的注释:
@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}
使用Spring(没有Spring Boot),它应该有点复杂但是因为我很长一段时间没有使用没有Spring Boot的Spring,所以我不喜欢说一个愚蠢的事情。
在配置中添加PropertyPlaceholderConfigurer对我有用。
@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;
}
}
在测试课上
@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());
}
}