JUnitでテストするときにも、DIが動作するようにするには
#contents
* 基本設定 [#fa4f0348]
pom.xml
#code(html){{
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.3.RELEASE</version>
<scope>test</scope>
</dependency>
}}
テストクラス
#code(java){{
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を取得するには [#o637874a]
@Autowiredで注入
#code(java){{
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class FooTest {
@Autowired
BarBean barBean;
}
}}
または、ApplicationContextから取得
#code(java){{
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class FooTest {
@Autowired
ApplicationContext ctx;
@Test
public void testFoo() {
BarBean instance = ctx.getAutowireCapableBeanFactory().createBean(BarBean.class);
}
}
}}
* Webアプリの場合 [#g4e652e8]
通常、XMLファイルは/WEB-INF/applicationContext.xmlに置くが、JUnitでのテスト時に参照しようとすると、file:でこのファイルを指定するしかなく、環境依存になってしまう。
XMLの置き場所をクラスパス階層内に変更すると、この問題が解消する。
web.xml
#code(html){{
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param>
}}
この例では、applicationContext.xmlは、クラスパスのルートに置く。
参照
- [[springの設定ファイルをWEB-INF直下に置くとテストが難しくなる>http://nabedge.blogspot.jp/2011/09/springweb-inf.html]]
* LoadTimeWeaverを使っている場合 [#e7129038]
SpringのXMLで以下のような設定をしてLoadTimeWeaverを使っている場合
#code(html){{
<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
#code(html){{
<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 の設定をテストクラス間で使いまわす [#mf560e48]
方法1 独自にアノテーションを作る
#code(java){{
@ContextConfiguration({"classpath:/applicationContext.xml"})
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestConfiguration {
}
}}
方法2 @ContextConfigurationを付けたクラスを共通の親クラスとする。Springのマニュアルでは[[この方法を紹介している>http://docs.spring.io/spring/docs/4.0.2.RELEASE/spring-framework-reference/html/testing.html#testcontext-ctx-management-inheritance]]。
#code(java){{
@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を指定する(らしい)。
* 運用時の設定をテスト用の設定で上書きする [#tca401b8]
→ [[Spring#r5b27951]]
* ApplicationContextのキャッシュ(Beanのキャッシュ)を破棄する [#h951e233]
設定が同じ範囲でApplicationContextはキャッシュされ使いまわされるようになっており、テストでsingletonのBeanの状態を変更してしまうと、その影響が他のテストに及ぶ。
singletonなBeanの状態を変更してしまうなど、Beanを「汚す」テストには、@DirtiesContextを付けると、テスト終了後にキャッシュが破棄されるようになる。
- クラスに @DirtiesContext → そのクラスのテストが終わったらキャッシュ破棄
- メソッドに @DirtiesContext → そのメソッドのテストが終わったらキャッシュ破棄
- クラスに @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) → そのクラスのそれぞれのメソッドについて、メソッドのテストが終わったらキャッシュ破棄
参照
- http://docs.spring.io/spring/docs/4.0.2.RELEASE/spring-framework-reference/html/testing.html#testcontext-ctx-management-caching
- http://docs.spring.io/spring/docs/4.0.2.RELEASE/spring-framework-reference/html/testing.html#integration-testing-annotations-spring \\
の@DirtiesContextの説明