2013-06-27 42 views
105

我正在嘗試爲我的程序中使用的一個簡單bean編寫單元測試來驗證表單。這個bean標註有@Component並且具有使用初始化類變量@Value("${this.property.value}") private String thisProperty;在單元測試期間填充Spring @Value

我想編寫單元測試這個類裏面的驗證方法,但是,如果可能的話,我想這樣做沒有使用屬性文件。我的理由是,如果我從屬性文件中提取的值發生更改,我希望不會影響我的測試用例。我的測試用例正在測試驗證值的代碼,而不是值本身。

有沒有一種方法可以在我的測試類中使用Java代碼來初始化一個Java類並填充該類中的Spring @Value屬性,然後使用它來測試?

我確實發現這個似乎很接近的How To,但仍然使用屬性文件。我寧願它都是Java代碼。

謝謝

回答

88

如果可能的話,我會嘗試寫沒有Spring上下文的測試。如果你在沒有彈簧的情況下在你的測試中創建這個類,那麼你可以完全控制它的字段。

要設置@value字段,您可以使用Springs ReflectionTestUtils - 它有一個方法setField來設置專用字段。

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

+1

正是我一直在試圖做什麼,我一直在尋找的設置我的課裏面的價值,謝謝! – Kyle

+1

或者即使沒有Spring依賴關係,也可以將該字段更改爲默認訪問(程序包保護),以使其可以輕鬆訪問測試。 –

+2

例如:'org.springframework.test.util.ReflectionTestUtils.setField(classUnderTest,「field」,「value」);' – Olivier

36

如果你想,你仍然可以Spring上下文中運行測試,並設置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內運行測試,這更簡單明瞭。

+2

我同意大多數邏輯應該用Mockito進行測試。我希望有更好的方式來測試註釋的存在和正確性,而不是通過Spring來運行測試。 – Altair7852

15

這似乎是工作,雖然還是有點冗長(我想還是其他更短):

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

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

我認爲這個答案更清晰,因爲它是Spring不可知論的,它適用於不同的場景,比如當你必須使用自定義測試運行器並且不能只添加'@ TestProperty'註釋。 – raspacorp

4

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

}

57

由於彈簧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(); 
    } 

    } 

} 

注:因此,有必要有org.springframework.context.support.PropertySourcesPlaceholderConfigurer比如在Spring上下文

編輯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); 
    } 

} 
+0

謝謝,終於有人回答瞭如何重寫Value而不是如何設置字段。我從PostConstruct中的字符串字段獲取值,因此我需要由Spring設置字符串值,而不是在構建之後。 – tequilacat

+0

我很樂意提供幫助 –