Spring In JUnit HelloWorld!

在寫這篇文章前,我只使用Spring Test去測試過我們的REST API。某一日,因為看到同事誤用的@ActiveProfile,讓我發現了其它不錯的Test Annotation。本篇文章分享給大家一些基本寫法。

Note. 我使用的spring-test版本是4.3.6。

下面這段是在修改之前的Load與Close Spring Context的程式碼,會註冊兩個Config,並將我們需要的物件綁定到此TestCase物件上:

private AnnotationConfigApplicationContext springContext;
@Before
public void setup(){
	springContext = new AnnotationConfigApplicationContext();
	springContext.setEnvironment(env);
	springContext.register(
			ModelConfig.class,
			MockConfig.class
	);
 
	springContext.refresh();
 
	springContext.getAutowireCapableBeanFactory().autowireBean(this);
}
 
@After
public void teardown(){
	springContext.close();
}
上面冗長的程式碼,只要使用以下Annotation,就可以達到Load與Close:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes={ModelConfig.class, MyTest.MockConfig.class})
public class RedfishSpringConfigTest {
	// skip
}
Runner要使用SpringRunner,而ContextConfiguration可以指定你要載入的Config class;假如你的config是xml形式,可以使用下面的寫法讓它去你的classpath找設定檔:
@ContextConfiguration("myconfig.xml")

在執行測試過程中,一定會需要從Spring Context中取得你需要的Bean進行測試,以下是舊的寫法:

AppController controller = springContext.getBean(AppController.class);
在透過SpringRunner執行測試案例後,你可以直接在測試案例中綁定資源,以下是@Autowired寫法:
@Autowired
private AppController appController;

在你的Spring Config中,你可能會讀取properties並透過Environment去取得你要的參數;而測試中也免不了需要賦予這些參數來確認功能是否正常,以下是舊的寫法:

MockEnvironment env = new MockEnvironment();
env.setProperty("interval", String.valueOf(expectedInterval));
env.setProperty("timeout", String.valueOf(expectedTimeout));
 
springContext = new AnnotationConfigApplicationContext();
springContext.setEnvironment(env);
假如要將測試的MockEnvironment帶入Spring Context中,這時候就要依靠ApplicationContextInitializer:
public static class EnvInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		MockEnvironment env = new MockEnvironment();
		env.setProperty("interval", String.valueOf(expectedInterval));
		env.setProperty("timeout", String.valueOf(expectedTimeout));
		applicationContext.setEnvironment(env);
	}
}
最後就是透過@ContextConfiguration載入這個EvnInitializer:
@ContextConfiguration(classes={ModelConfig.class, MyTest.MockConfig.class},initializers=MyTest.EvnInitializer.class)
嗯..似乎要寫更多的code。其實還有另外一個方案,就是使用@TestPropertySource去指定參數:
@TestPropertySource(properties={"interval=123","timeout=456"})
但使用@TestPropertySource,如果要串變數上去,反而可讀性就不是這麼好了。

在舊的寫法中,Spring Context的生命週期取決於我將Load與Close放在哪裡;而spring-text則是提供了@DirtiesContext讓你設定設定生命週期。用法如下,我指定在整個test suite執行後才清除context:

@DirtiesContext(classMode=ClassMode.AFTER_CLASS)
除了上面這個,常會使用的還有ClassMode.AFTER_EACH_TEST_METHOD。至於要用哪一種,可以好好思考,畢竟Spring的Load與Close是相當花時間的。