web-dev-qa-db-ja.com

単体テスト中のSpring @Valueの生成

私は、フォームを検証するために私のプログラムで使われている単純なBeanのための単体テストを書きたいと思います。このBeanには@Componentという注釈が付けられており、@Value("${this.property.value}") private String thisProperty;を使用して初期化されるクラス変数があります。

このクラス内の検証メソッドのためのユニットテストを書きたいのですが、可能であれば、プロパティファイルを使用せずにそうしたいと思います。この理由は、プロパティファイルから取得した値が変更されても、テストケースに影響がないようにするためです。私のテストケースは、値そのものではなく、値を検証するコードをテストすることです。

テストクラス内でJavaコードを使用してJavaクラスを初期化し、そのクラス内にSpring @Valueプロパティを設定してからそれを使用してテストする方法はありますか。

私はこれを見つけました How To それは近いようですが、それでもプロパティファイルを使います。私はそれをすべてJavaコードにしたいと思います。

ありがとう

175
Kyle

可能であれば、私はSpring Contextを使わずにそれらのテストを書くことを試みるでしょう。テストなしでこのクラスを作成した場合は、そのフィールドを完全に制御できます。

@valueフィールドを設定するには、Springs ReflectionTestUtils - プライベートフィールドを設定するメソッド setField を使用します。

@see JavaDoc:ReflectionTestUtils.setField(Java.lang.Object、Java.lang.String、Java.lang.Object)

154
Ralph

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

}
134

必要に応じて、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の内部ではまったくテストを実行しません。これはより明確で単純です。

46

まだ少し冗長ですが、これはうまくいくようです(まだもっと短いものが欲しいのですが)。

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

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

あなたのクラスをユニタリーと統合の両方でテスト可能にする

単純な単体テスト(つまり、実行中の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(){
      ...
   }
}

統合テスト例

@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を長い間使っていなかったので、私は愚かなことを言うのは嫌いです。

14
davidxxx

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());
    }
}
4
fjkjava