首页 文章

在单元测试期间填充Spring @Value

提问于
浏览
145

我在我的程序中使用'm trying to write a Unit Test for a simple bean that'来验证表单 . bean使用 @Component 进行批注,并且具有使用 @Value("${this.property.value}") private String thisProperty; 初始化的类变量

我想为这个类中的验证方法编写单元测试,但是,如果可能的话,我想在不使用属性文件的情况下这样做 . 我的理由是,如果我从属性文件中提取的值发生变化,我希望这不会影响我的测试用例 . 我的测试用例是测试验证值的代码,而不是值本身 .

有没有办法在我的测试类中使用Java代码初始化Java类并在该类中填充Spring @Value属性然后使用它来测试?

我确实发现这个似乎很接近的How To,但仍然使用属性文件 . 我宁愿这一切都是Java代码 .

谢谢

6 回答

  • 26

    如果可能,我会尝试在没有Spring Context的情况下编写这些测试 . 如果在没有 spring 的情况下在测试中创建此类,则可以完全控制其字段 .

    要设置 @value 字段,可以使用Springs ReflectionTestUtils - 它有一个方法setField来设置私有字段 .

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

  • 44

    从Spring 4.1开始,您可以在单元测试类级别上使用 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();
        }
    
      }
    
    }
    

    Note: 必须在Spring上下文中包含 org.springframework.context.support.PropertySourcesPlaceholderConfigurer 的实例

    Edit 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);
      }
    
    }
    
  • 4

    如果需要,您仍然可以在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内部运行测试,这更加清晰和简单 .

  • 4

    这似乎有效,虽然仍然有点冗长(我还想要更短的东西):

    @BeforeClass
    public static void beforeClass() {
        System.setProperty("some.property", "<value>");
    }
    
    // Optionally:
    @AfterClass
    public static void afterClass() {
        System.clearProperty("some.property");
    }
    
  • 135

    在配置中添加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());
    }
    

    }

  • 109

    Make your class testable both unitary and in integration

    为了能够编写普通单元测试(没有运行 spring 容器)和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;
        }
    
        //...         
    }
    

    Unit test example

    你可以在不使用Spring的情况下实例化 Foo 并为 property 注入任何值,这要归功于构造函数:

    public class FooTest{
    
       Foo foo = new Foo("dummyValue");
    
       @Test
       public void doThat(){
          ...
       }
    }
    

    Integration test example

    由于 @SpringBootTestproperties 属性,您可以以这种简单的方式在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,所以我不喜欢说一个愚蠢的事情 .

相关问题