javascript
afterclass_通过beforeClass和afterClass设置增强Spring Test Framework
afterclass
如何允許實例方法作為JUnit BeforeClass行為運行JUnit允許您在所有測試方法調用之前和之后一次在類級別上設置方法。 但是,通過有意設計,它們將其限制為僅使用@BeforeClass和@AfterClass批注的靜態方法。 例如,以下簡單演示演示了典型的Junit設置:
package deng.junitdemo;import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test;public class DemoTest {@Testpublic void testOne() {System.out.println('Normal test method #1.');}@Testpublic void testTwo() {System.out.println('Normal test method #2.');}@BeforeClasspublic static void beforeClassSetup() {System.out.println('A static method setup before class.');}@AfterClasspublic static void afterClassSetup() {System.out.println('A static method setup after class.');} }并應產生以下輸出:
A static method setup before class. Normal test method #1. Normal test method #2. A static method setup after class.在大多數情況下,此用法都可以,但是有時候您想使用非靜態方法來設置測試。 稍后,我將向您展示更詳細的用例,但現在,讓我們看看如何首先使用JUnit解決這個頑皮的問題。 我們可以通過使測試實現一個提供before和after回調的Listener來解決此問題,并且需要挖掘JUnit來檢測此Listener來調用我們的方法。 這是我想出的解決方案:
package deng.junitdemo;import org.junit.Test; import org.junit.runner.RunWith;@RunWith(InstanceTestClassRunner.class) public class Demo2Test implements InstanceTestClassListener {@Testpublic void testOne() {System.out.println('Normal test method #1');}@Testpublic void testTwo() {System.out.println('Normal test method #2');}@Overridepublic void beforeClassSetup() {System.out.println('An instance method setup before class.');}@Overridepublic void afterClassSetup() {System.out.println('An instance method setup after class.');} }如上所述,我們的監聽器是一個簡單的合同:
package deng.junitdemo;public interface InstanceTestClassListener {void beforeClassSetup();void afterClassSetup(); }我們的下一個任務是提供將觸發設置方法的JUnit運行器實現。
package deng.junitdemo;import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.InitializationError;public class InstanceTestClassRunner extends BlockJUnit4ClassRunner {private InstanceTestClassListener InstanceSetupListener;public InstanceTestClassRunner(Class<?> klass) throws InitializationError {super(klass);}@Overrideprotected Object createTest() throws Exception {Object test = super.createTest();// Note that JUnit4 will call this createTest() multiple times for each// test method, so we need to ensure to call 'beforeClassSetup' only once.if (test instanceof InstanceTestClassListener && InstanceSetupListener == null) {InstanceSetupListener = (InstanceTestClassListener) test;InstanceSetupListener.beforeClassSetup();}return test;}@Overridepublic void run(RunNotifier notifier) {super.run(notifier);if (InstanceSetupListener != null)InstanceSetupListener.afterClassSetup();} }現在我們從事業務。 如果我們在測試之上運行,它應該會給我們類似的結果,但是這次我們使用的是實例方法!
An instance method setup before class. Normal test method #1 Normal test method #2 An instance method setup after class.
一個具體的用例:使用Spring Test Framework
現在,讓我向您展示一個上面的真實用例。 如果使用Spring Test Framework,通常會設置一個這樣的測試,以便可以將測試夾具作為成員實例注入。
package deng.junitdemo.spring;import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class SpringDemoTest {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));} }您還需要在同一包下的spring xml才能運行:
<?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans'xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd'><bean id='myList' class='java.util.ArrayList'><constructor-arg><list><value>one</value><value>two</value></list></constructor-arg></bean> </beans>非常注意成員實例List<String> myList 。 運行JUnit測試時,Spring將注入該字段,并且可以在任何測試方法中使用它。 但是,如果您想一次性設置一些代碼并獲得對Spring注入字段的引用,那么您很不幸。 這是因為JUnit @BeforeClass將強制您的方法為靜態方法。 如果您將字段設為靜態,則在測試中無法使用Spring注入!
現在,如果您是經常使用Spring的用戶,您應該知道Spring Test Framework已經為您提供了一種處理此類用例的方法。 這是一種使用Spring樣式進行類級別設置的方法:
package deng.junitdemo.spring;import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;@RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, SpringDemo2Test.class}) @ContextConfiguration public class SpringDemo2Test extends AbstractTestExecutionListener {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));}@Overridepublic void afterTestClass(TestContext testContext) {List<?> list = testContext.getApplicationContext().getBean('myList', List.class);assertThat((String)list.get(0), is('one'));}@Overridepublic void beforeTestClass(TestContext testContext) {List<?> list = testContext.getApplicationContext().getBean('myList', List.class);assertThat((String)list.get(1), is('two'));} }如您所見,Spring提供了@TestExecutionListeners批注,以允許您編寫任何偵聽器,并且在其中將具有對TestContext的引用,該引用具有ApplicationContext以便您獲取注入的字段引用。 這行得通,但我覺得它不是很優雅。 當您注入的字段已經可以用作字段時,它會強制您查找bean。 但是除非您通過TestContext參數,否則您將無法使用它。
現在,如果您混合了開始時提供的解決方案,我們將看到更漂亮的測試設置。 讓我們來看看它:
package deng.junitdemo.spring;import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration;import deng.junitdemo.InstanceTestClassListener;@RunWith(SpringInstanceTestClassRunner.class) @ContextConfiguration public class SpringDemo3Test implements InstanceTestClassListener {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));}@Overridepublic void beforeClassSetup() {assertThat((String)myList.get(0), is('one'));}@Overridepublic void afterClassSetup() {assertThat((String)myList.get(1), is('two'));} }現在,JUnit僅允許您使用單個Runner ,因此我們必須擴展Spring的版本以插入之前的操作。
package deng.junitdemo.spring;import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import deng.junitdemo.InstanceTestClassListener;public class SpringInstanceTestClassRunner extends SpringJUnit4ClassRunner {private InstanceTestClassListener InstanceSetupListener;public SpringInstanceTestClassRunner(Class<?> clazz) throws InitializationError {super(clazz);}@Overrideprotected Object createTest() throws Exception {Object test = super.createTest();// Note that JUnit4 will call this createTest() multiple times for each// test method, so we need to ensure to call 'beforeClassSetup' only once.if (test instanceof InstanceTestClassListener && InstanceSetupListener == null) {InstanceSetupListener = (InstanceTestClassListener) test;InstanceSetupListener.beforeClassSetup();}return test;}@Overridepublic void run(RunNotifier notifier) {super.run(notifier);if (InstanceSetupListener != null)InstanceSetupListener.afterClassSetup();} }這應該夠了吧。 運行測試將使用以下輸出:
12:58:48 main INFO org.springframework.test.context.support.AbstractContextLoader:139 | Detected default resource location 'classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml' for test class [deng.junitdemo.spring.SpringDemo3Test]. 12:58:48 main INFO org.springframework.test.context.support.DelegatingSmartContextLoader:148 | GenericXmlContextLoader detected default locations for context configuration [ContextConfigurationAttributes@74b23210 declaringClass = 'deng.junitdemo.spring.SpringDemo3Test', locations = '{classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml}', classes = '{}', inheritLocations = true, contextLoaderClass = 'org.springframework.test.context.ContextLoader']. 12:58:48 main INFO org.springframework.test.context.support.AnnotationConfigContextLoader:150 | Could not detect default configuration classes for test class [deng.junitdemo.spring.SpringDemo3Test]: SpringDemo3Test does not declare any static, non-private, non-final, inner classes annotated with @Configuration. 12:58:48 main INFO org.springframework.test.context.TestContextManager:185 | @TestExecutionListeners is not present for class [class deng.junitdemo.spring.SpringDemo3Test]: using defaults. 12:58:48 main INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 | Loading XML bean definitions from class path resource [deng/junitdemo/spring/SpringDemo3Test-context.xml] 12:58:48 main INFO org.springframework.context.support.GenericApplicationContext:500 | Refreshing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 29 12:58:48 EDT 2012]; root of context hierarchy 12:58:49 main INFO org.springframework.beans.factory.support.DefaultListableBeanFactory:581 | Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@73c6641: defining beans [myList,org.springframework.context.annotation. internalConfigurationAnnotationProcessor,org. springframework.context.annotation.internalAutowiredAnnotationProcessor,org .springframework.context.annotation.internalRequiredAnnotationProcessor,org. springframework.context.annotation.internalCommonAnnotationProcessor,org. springframework.context.annotation. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy 12:58:49 Thread-1 INFO org.springframework.context.support.GenericApplicationContext:1025 | Closing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 29 12:58:48 EDT 2012]; root of context hierarchy 12:58:49 Thread-1 INFO org.springframework.beans.factory.support. DefaultListableBeanFactory:433 | Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ 73c6641: defining beans [myList,org.springframework.context.annotation. internalConfigurationAnnotationProcessor,org.springframework. context.annotation.internalAutowiredAnnotationProcessor,org.springframework. context.annotation.internalRequiredAnnotationProcessor,org.springframework. context.annotation.internalCommonAnnotationProcessor,org.springframework. context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy 顯然,輸出在這里沒有顯示任何有趣的內容,但是測試應該在所有聲明通過的情況下運行。 關鍵是,現在我們有一種更優雅的方法來調用類級別的測試之前和之后的測試,并且它們可以是允許Spring注入的實例方法。
下載演示代碼
您可能會從我的沙箱中獲得一個正常運行的Maven項目中的演示代碼
參考: A程序員雜志博客上的JCG合作伙伴 Zemian Deng提供的beforeClass和afterClass設置增強了Spring Test Framework 。
翻譯自: https://www.javacodegeeks.com/2012/10/enhancing-spring-test-framework-with.html
afterclass
總結
以上是生活随笔為你收集整理的afterclass_通过beforeClass和afterClass设置增强Spring Test Framework的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果用安卓软件有什么办法吗(苹果用安卓软
- 下一篇: linux .pc文件(linux .p