私は、フォームを検証するために私のプログラムで使われている単純な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以降、ユニットテストクラスレベルで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
のインスタンスが必要です
2017年8月24日編集:SpringBoot 1.4.0以降を使用している場合は、 @SpringBootTest
および - でテストを初期化できます @SpringBootConfiguration
アノテーション。さらに詳しい情報 ここ
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コンポーネントクラスの統合テストの両方を作成できるようにするには、このクラスを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(){
...
}
}
統合テスト例
@SpringBootTest
のproperties
属性のおかげで、この簡単な方法で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に追加することは私にとってはうまくいきます。
@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());
}
}