JUnit позволяет вам настраивать методы на уровне класса до и после вызова всех методов тестирования.  Однако специально они ограничивают это только статическими методами с использованием аннотаций @BeforeClass и @AfterClass .  Например, эта простая демонстрация показывает типичную настройку Junit: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | packagedeng.junitdemo;importorg.junit.AfterClass;importorg.junit.BeforeClass;importorg.junit.Test;publicclassDemoTest {    @Test    publicvoidtestOne() {        System.out.println('Normal test method #1.');    }    @Test    publicvoidtestTwo() {        System.out.println('Normal test method #2.');    }    @BeforeClass    publicstaticvoidbeforeClassSetup() {        System.out.println('A static method setup before class.');    }    @AfterClass    publicstaticvoidafterClassSetup() {        System.out.println('A static method setup after class.');    }} | 
И выше должен привести следующий вывод:
| 1 2 3 4 | A staticmethod setup before class.Normal test method #1.Normal test method #2.A staticmethod setup after class. | 
Такое использование подходит в большинстве случаев, но бывают случаи, когда вы хотите использовать нестатические методы для настройки теста. Позже я покажу вам более подробный пример использования, но сейчас давайте посмотрим, как мы можем сначала решить эту непослушную проблему с помощью JUnit. Мы можем решить эту проблему, сделав тест реализующий Listener, который обеспечивает обратные вызовы до и после, и нам нужно будет копаться в JUnit, чтобы обнаружить этот Listener для вызова наших методов. Это решение, которое я придумал:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | packagedeng.junitdemo;importorg.junit.Test;importorg.junit.runner.RunWith;@RunWith(InstanceTestClassRunner.class)publicclassDemo2Test implementsInstanceTestClassListener {    @Test    publicvoidtestOne() {        System.out.println('Normal test method #1');    }    @Test    publicvoidtestTwo() {        System.out.println('Normal test method #2');    }    @Override    publicvoidbeforeClassSetup() {        System.out.println('An instance method setup before class.');    }    @Override    publicvoidafterClassSetup() {        System.out.println('An instance method setup after class.');    }} | 
Как указано выше, наш слушатель представляет собой простой контракт:
| 1 2 3 4 5 6 | packagedeng.junitdemo;publicinterfaceInstanceTestClassListener {    voidbeforeClassSetup();    voidafterClassSetup();} | 
Наша следующая задача — предоставить реализацию бегуна JUnit, которая будет запускать методы установки.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | packagedeng.junitdemo;importorg.junit.runner.notification.RunNotifier;importorg.junit.runners.BlockJUnit4ClassRunner;importorg.junit.runners.model.InitializationError;publicclassInstanceTestClassRunner extendsBlockJUnit4ClassRunner {    privateInstanceTestClassListener InstanceSetupListener;    publicInstanceTestClassRunner(Class<?> klass) throwsInitializationError {        super(klass);    }    @Override    protectedObject createTest() throwsException {        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 instanceofInstanceTestClassListener && InstanceSetupListener == null) {            InstanceSetupListener = (InstanceTestClassListener) test;            InstanceSetupListener.beforeClassSetup();        }        returntest;    }    @Override    publicvoidrun(RunNotifier notifier) {        super.run(notifier);        if(InstanceSetupListener != null)            InstanceSetupListener.afterClassSetup();    }} | 
Сейчас мы находимся в бизнесе. Если мы запустим тест выше, он должен дать нам аналогичный результат, но на этот раз мы используем методы экземпляра!
| 1 2 3 4 | An instance method setup before class.Normal test method #1Normal test method #2An instance method setup after class. | 
 
  Конкретный вариант использования: работа с Spring Test Framework 
Теперь позвольте мне показать вам реальный пример использования выше. Если вы используете Spring Test Framework, вы обычно настраиваете тест, подобный этому, чтобы в качестве экземпляра элемента можно было вставить тестовое устройство.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | packagedeng.junitdemo.spring;importstaticorg.hamcrest.Matchers.is;importstaticorg.junit.Assert.assertThat;importjava.util.List;importjavax.annotation.Resource;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.test.context.ContextConfiguration;importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublicclassSpringDemoTest {    @Resource(name='myList')    privateList<String> myList;    @Test    publicvoidtestMyListInjection() {        assertThat(myList.size(), is(2));    }} | 
Вам также понадобится Spring xml под тем же пакетом для запуска выше:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 | <?xmlversion='1.0'encoding='UTF-8'?>     xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd'>     <beanid='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 Injection не будет работать в вашем тесте! 
Теперь, если вы частый пользователь Spring, вы должны знать, что Spring Test Framework уже предоставил вам способ справиться с этим типом сценария использования. Вот способ настройки уровня класса в стиле Spring:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | packagedeng.junitdemo.spring;importstaticorg.hamcrest.Matchers.is;importstaticorg.junit.Assert.assertThat;importjava.util.List;importjavax.annotation.Resource;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.test.context.ContextConfiguration;importorg.springframework.test.context.TestContext;importorg.springframework.test.context.TestExecutionListeners;importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;importorg.springframework.test.context.support.AbstractTestExecutionListener;importorg.springframework.test.context.support.DependencyInjectionTestExecutionListener;@RunWith(SpringJUnit4ClassRunner.class)@TestExecutionListeners(listeners = {        DependencyInjectionTestExecutionListener.class,         SpringDemo2Test.class})@ContextConfigurationpublicclassSpringDemo2Test extendsAbstractTestExecutionListener {    @Resource(name='myList')    privateList<String> myList;    @Test    publicvoidtestMyListInjection() {        assertThat(myList.size(), is(2));    }    @Override    publicvoidafterTestClass(TestContext testContext) {        List<?> list = testContext.getApplicationContext().getBean('myList', List.class);        assertThat((String)list.get(0), is('one'));    }    @Override    publicvoidbeforeTestClass(TestContext testContext) {        List<?> list = testContext.getApplicationContext().getBean('myList', List.class);        assertThat((String)list.get(1), is('two'));    }} | 
  Как вы можете видеть, Spring предлагает аннотацию @TestExecutionListeners чтобы позволить вам написать любого Слушателя, и в нем у вас будет ссылка на TestContext который имеет ApplicationContext для вас, чтобы перейти к TestContext ссылке на поле.  Это работает, но я нахожу это не очень элегантным.  Это заставляет вас искать бин, в то время как ваше введенное поле уже доступно как поле.  Но вы не можете использовать его, если не TestContext через параметр TestContext . 
Теперь, если вы смешаете решение, которое мы предоставили в начале, мы увидим более приятную настройку теста. Давай увидим это:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | packagedeng.junitdemo.spring;importstaticorg.hamcrest.Matchers.is;importstaticorg.junit.Assert.assertThat;importjava.util.List;importjavax.annotation.Resource;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.test.context.ContextConfiguration;importdeng.junitdemo.InstanceTestClassListener;@RunWith(SpringInstanceTestClassRunner.class)@ContextConfigurationpublicclassSpringDemo3Test implementsInstanceTestClassListener {    @Resource(name='myList')    privateList<String> myList;    @Test    publicvoidtestMyListInjection() {        assertThat(myList.size(), is(2));    }    @Override    publicvoidbeforeClassSetup() {        assertThat((String)myList.get(0), is('one'));    }    @Override    publicvoidafterClassSetup() {        assertThat((String)myList.get(1), is('two'));    }} | 
  Теперь JUnit позволяет вам использовать только один Runner , поэтому мы должны расширить версию Spring, чтобы вставить то, что мы делали раньше. 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | packagedeng.junitdemo.spring;importorg.junit.runner.notification.RunNotifier;importorg.junit.runners.model.InitializationError;importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;importdeng.junitdemo.InstanceTestClassListener;publicclassSpringInstanceTestClassRunner extendsSpringJUnit4ClassRunner {    privateInstanceTestClassListener InstanceSetupListener;    publicSpringInstanceTestClassRunner(Class<?> clazz) throwsInitializationError {        super(clazz);    }    @Override    protectedObject createTest() throwsException {        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 instanceofInstanceTestClassListener && InstanceSetupListener == null) {            InstanceSetupListener = (InstanceTestClassListener) test;            InstanceSetupListener.beforeClassSetup();        }        returntest;    }    @Override    publicvoidrun(RunNotifier notifier) {        super.run(notifier);        if(InstanceSetupListener != null)            InstanceSetupListener.afterClassSetup();    }} | 
Это должно делать свое дело. Запуск теста даст использование этого вывода:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 12:58:48main INFO  org.springframework.test.context.support.AbstractContextLoader:139| Detected defaultresource location 'classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml'fortest class[deng.junitdemo.spring.SpringDemo3Test].12:58:48main INFO  org.springframework.test.context.support.DelegatingSmartContextLoader:148| GenericXmlContextLoader detected defaultlocations forcontext configuration [ContextConfigurationAttributes@74b23210declaringClass = 'deng.junitdemo.spring.SpringDemo3Test', locations = '{classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml}', classes = '{}', inheritLocations = true, contextLoaderClass = 'org.springframework.test.context.ContextLoader'].12:58:48main INFO  org.springframework.test.context.support.AnnotationConfigContextLoader:150| Could not detect defaultconfiguration classes fortest class[deng.junitdemo.spring.SpringDemo3Test]: SpringDemo3Test does not declare any static, non-private, non-final, inner classes annotated with @Configuration.12:58:48main INFO  org.springframework.test.context.TestContextManager:185| @TestExecutionListenersis not present forclass[classdeng.junitdemo.spring.SpringDemo3Test]: using defaults.12:58:48main INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315| Loading XML bean definitions from classpath resource [deng/junitdemo/spring/SpringDemo3Test-context.xml]12:58:48main INFO  org.springframework.context.support.GenericApplicationContext:500| Refreshing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 2912:58:48EDT 2012]; root of context hierarchy12:58:49main 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 hierarchy12:58:49Thread-1INFO  org.springframework.context.support.GenericApplicationContext:1025| Closing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 2912:58:48EDT 2012]; root of context hierarchy12:58:49Thread-1INFO  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 из моей песочницы.
Ссылка: Улучшение Spring Test Framework с установкой beforeClass и afterClass от нашего партнера JCG Земьяна Дена в блоге A Programmer’s Journal .