Запуск приложения до и после Suite в jUnit 4.x

Я пытаюсь выполнить предварительную настройку и отключение для набора интеграционных тестов, используя jUnit 4.4 для выполнения тестов. Срыв должен выполняться надежно. У меня другие проблемы с TestNG, поэтому я ищу порт для jUnit. Какие крючки доступны для выполнения до запуска любых тестов и после завершения всех тестов?

Примечание. Мы используем maven 2 для нашей сборки. Я пробовал использовать фазы maven pre- и post-integration-test, но если тест завершился неудачно, maven останавливается и не запускает post-integration-test, что не помогает.

Ответ 1

Да, можно надежно запускать методы настройки и сбрасывания до и после любых тестов в наборе тестов. Позвольте мне продемонстрировать код:

package com.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

Итак, ваш класс Test1 будет выглядеть примерно так:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... и вы можете себе представить, что Test2 похож. Если вы запустили TestSuite, вы получите:

setting up
test1
test2
tearing down

Итак, вы можете видеть, что установка/отключение запускается только до и после всех тестов, соответственно.

Ловушка: это работает только в том случае, если вы используете тестовый набор, а не для тестирования Test1 и Test2 как отдельных тестов JUnit. Вы упомянули, что используете maven, и плагин maven surefire любит запускать тесты по отдельности, а не в составе пакета. В этом случае я бы рекомендовал создать суперкласс, который расширяется каждый тестовый класс. Суперкласс затем содержит аннотированные методы @BeforeClass и @AfterClass. Хотя это не так чисто, как выше описанный метод, я думаю, он будет работать для вас.

Что касается проблемы с неудачными тестами, вы можете установить maven.test.error.ignore, чтобы сборка продолжалась в случае неудачных тестов. Это не рекомендуется в качестве постоянной практики, но оно должно заставить вас функционировать до тех пор, пока все ваши тесты не пройдут. Более подробно см. документацию maven surefire.

Ответ 2

Один из моих коллег предложил следующее: вы можете использовать собственный RunListener и реализовать метод testRunFinished(): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org.junit.runner.Result)

Чтобы зарегистрировать RunListener, просто настройте плагин surefire следующим образом: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html раздел "Использование пользовательских прослушивателей и репортеров"

Эта конфигурация также должна быть выбрана отказоустойчивым плагином. Это решение замечательно, потому что вам не нужно указывать классы, поисковые тесты или любой из этих материалов - это позволяет Maven делать свою магию, ожидая завершения всех тестов.

Ответ 4

Используя аннотации, вы можете сделать что-то вроде этого:

import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}

Ответ 5

Здесь мы

  • обновлен до JUnit 4.5,
  • написал аннотации, чтобы пометить каждый тестовый класс или метод, которым нужна рабочая служба,
  • написал обработчики для каждой аннотации, содержащие статические методы для реализации настройки и удаления службы,
  • расширил обычный Runner, чтобы найти аннотации на тестах, добавив методы статического обработчика в цепочку выполнения теста в соответствующих точках.

Ответ 6

При условии, что все ваши тесты могут расширить "технический" класс и находиться в одном пакете, вы можете сделать небольшой трюк:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

Помните, что это решение имеет множество недостатков:

  • должен выполнить все тесты пакета
  • должен подклассифицировать класс "techincal"
  • вы не можете использовать @BeforeClass и @AfterClass внутри подклассов
  • если вы выполняете только один тест в пакете, очистка не выполняется
  • ...

Для справки: listClassesIn() = > Как вы находите все подклассы данного класса в Java?

Ответ 7

Что касается "Примечание: мы используем maven 2 для нашей сборки. Я пробовал использовать фазы pre-and post-integration-test maven, но, если тест завершился неудачно, maven останавливается и не запускает пост- интеграция-тест, который не помогает".

вместо этого вы можете попробовать защищенный плагин, я думаю, что у него есть возможность обеспечить очистку независимо от настройки или состояния промежуточной стадии

Ответ 8

Единственный способ, по которому я думаю, получить необходимую функциональность, - это сделать что-то вроде

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

Я использую что-то подобное в eclipse, поэтому я не уверен, насколько он переносится вне этой среды

Ответ 9

Насколько я знаю, в JUnit нет механизма для этого, однако вы можете попробовать подклассифицировать Suite и переопределить метод run() версией, которая обеспечивает привязки.

Ответ 10

Так как maven-surefire-plugin не запускает класс класса сначала, но рассматривает классы классов и тестов одинаково, поэтому мы можем настроить плагин, как показано ниже, чтобы включить только классы классов и отключить все тесты. Suite выполнит все тесты.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>

Ответ 11

Если вы не хотите создавать набор и должны перечислять все свои тестовые классы, вы можете использовать рефлексию для динамического поиска количества тестовых классов и обратного отсчета в базовом классе @AfterClass, чтобы сделать tearDown только один раз:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

Если вы предпочитаете использовать @BeforeClass вместо статических блоков, вы также можете использовать булевский флаг для выполнения подсчета отражения и тестирования только один раз при первом вызове. Надеюсь, что это помогает кому-то, мне потребовался день, чтобы найти лучший способ, чем перечисление всех классов в пакете.

Теперь все, что вам нужно сделать, это расширить этот класс для всех ваших тестовых классов. У нас уже был базовый класс, чтобы предоставить некоторые общие вещи для всех наших тестов, поэтому это было лучшее решение для нас.

Вдохновение исходит из этого ответа SO fooobar.com/info/75792/...

Если вы не хотите распространять этот класс повсюду, этот последний ответ SO может делать то, что вы хотите.