JUnitでテストするときにも、DIが動作するようにするには
基本設定
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.3.RELEASE</version>
<scope>test</scope>
</dependency>
テストクラス
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/applicationContext.xml")
public class FooTest {
...
}
@ContextConfigurationでSpringのXML設定ファイルを指定
- デフォルトは、classpath:<テストクラスと同じパッケージ>/<クラス名>-context.xml
- パスの指定方法
- /で始まらない: クラスパス階層内から、現在のパッケージからの相対で
- /で始まる: クラスパス階層内から、パッケージを先頭から指定
- file:, classpath: から始まる: 指定のとおり
- 複数指定する場合は @ContextConfiguration({"/app-config.xml", "/test-config.xml"}) のように配列で指定。
- 2つのファイルで矛盾した設定があった場合どうなるかは未調査。
- XMLベースの変わりにJavaベースの設定をしている場合も、同様に@ContextConfigurationで指定する。詳細は下記の参照先を参照。
参照
- http://docs.spring.io/spring/docs/4.0.2.RELEASE/spring-framework-reference/htmlsingle/#testcontext-ctx-management
- http://gordondickens.com/wordpress/2011/01/07/junit-spring-what-you-dont-know-about/
- http://www.swiftmind.com/de/2011/06/22/spring-3-1-m2-testing-with-configuration-classes-and-profiles/
テストプログラム中でBeanを取得するには
@Autowiredで注入
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class FooTest {
@Autowired
BarBean barBean;
}
または、ApplicationContextから取得
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class FooTest {
@Autowired
ApplicationContext ctx;
@Test
public void testFoo() {
BarBean instance = ctx.getAutowireCapableBeanFactory().createBean(BarBean.class);
}
}
Webアプリの場合
通常、XMLファイルは/WEB-INF/applicationContext.xmlに置くが、JUnitでのテスト時に参照しようとすると、file:でこのファイルを指定するしかなく、環境依存になってしまう。
XMLの置き場所をクラスパス階層内に変更すると、この問題が解消する。
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param>
この例では、applicationContext.xmlは、クラスパスのルートに置く。
参照
LoadTimeWeaverを使っている場合
SpringのXMLで以下のような設定をしてLoadTimeWeaverを使っている場合
<context:load-time-weaver/>
テストを実行したときに以下のエラーが出る
Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method.
この場合は、テスト実行時のjavaコマンドに-javaagentオプションでspring-instrument.jarを指定する必要がある。Maven経由でテスト実行している場合、surefireプラグインに対してこの設定をする。spring-instrument.jarはMavenのdependencyの記述で取得できるが、取得した先のパスやJARファイル名を明示的にsurefireプラグインの設定に記述する必要があるらしい。
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>4.0.3.RELEASE</version>
<scope>test</scope>
</dependency>
(略)
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<argLine>
-javaagent:${settings.localRepository}/org/springframework/spring-instrument/4.0.3.RELEASE/spring-instrument-4.0.3.RELEASE.jar
</argLine>
</configuration>
</plugin>
</plugins>
</build>
この記述例では、surefireプラグインのargLineで、Mavenが取得したJARの置き場所を直接参照している。
パスやJARファイル名は、spring-instrumentのバージョンに依存して変わるので、dependencyで記述したバージョンに合わせて変更する必要がある。
参照
- http://stackoverflow.com/questions/7296627/springs-loadtimeweaver-agent-not-starting-up
- http://stackoverflow.com/questions/7996800/running-unit-tests-using-maven-in-spring-ltw-environment
@ContextConfiguration の設定をテストクラス間で使いまわす
方法1 独自にアノテーションを作る
@ContextConfiguration({"classpath:/applicationContext.xml"})
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestConfiguration {
}
方法2 @ContextConfigurationを付けたクラスを共通の親クラスとする。Springのマニュアルではこの方法を紹介している。
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestBase {
}
@RunWith(SpringJUnit4ClassRunner.class)
public class FooTest extends TestBase {
...
}
- @RunWithも個別のテストクラスで書かずに済むよう、TestBaseのほうに@RunWithを付けたくなるが、そうすると、TestBase自体がテストとして実行されてしまうのでNG。
なお、TestBaseを継承したクラスにも@ContextConfigurationを付ければ、SpringのXMLファイルが追加される。
親クラスの@ContextConfigurationを無視して、自分の指定だけ有効にするには、自分の@ContextConfigurationにinheritLocations = falseを指定する(らしい)。
運用時の設定をテスト用の設定で上書きする
ApplicationContextのキャッシュ(Beanのキャッシュ)を破棄する
設定が同じ範囲でApplicationContextはキャッシュされ使いまわされるようになっており、テストでsingletonのBeanの状態を変更してしまうと、その影響が他のテストに及ぶ。
singletonなBeanの状態を変更してしまうなど、Beanを「汚す」テストには、@DirtiesContextを付けると、テスト終了後にキャッシュが破棄されるようになる。
- クラスに @DirtiesContext → そのクラスのテストが終わったらキャッシュ破棄
- メソッドに @DirtiesContext → そのメソッドのテストが終わったらキャッシュ破棄
- クラスに @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) → そのクラスのそれぞれのメソッドについて、メソッドのテストが終わったらキャッシュ破棄
参照