Хук выполнения до и после Suite в jUnit 4.x


84

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

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


1
Для интеграционных тестов вы должны использовать maven-failsafe-plugin вместо surefire. Это не будет пропущено, post-integration-testесли тест не пройден. См. Также эту страницу вики .
Крис Х.

не могли бы вы поделиться своей финальной реализацией?
vikramvi 05

Ответы:


114

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

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
Это отлично сработало для меня, когда я вошел в maven-surefire-plugin и создал список включений, указывающий на набор, который я хотел запустить.
Jherico

2
Начиная с JUnit 4.8.2, это не очень хорошо работает с параметризованными тестами. Методы @BeforeClass Suite будут запускаться после метода @ Parameterized.Parameters теста, предотвращая любую зависимость от настройки Suite.
Anm

В ответ на мое собственное мнение при использовании @Theories вызов метода @DataPoints выполняется после @BeforeClass Suite.
Anm

19
Извините за некро, но добавление BeforeClass / AfterClass к суперклассу не работает должным образом - они по-прежнему вызываются после завершения каждого тестового класса. Это для потомков.
Субу Шанкара Субраманиан

3
Это все еще актуальный подход? Как избежать необходимости перечислять список тестовых классов в аннотации SuiteClasses?
Бурхан Али

34

Мой коллега предложил следующее: вы можете использовать собственный 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 творить чудеса, ожидая завершения всех тестов.


5
+1 Это первое удобное решение, которое я видел без громоздкого обслуживания класса люкс!
Stefan Haberl


6

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

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()
    {

    }

}

4
Настройку и демонтаж необходимо выполнять один раз за запуск. Это поможет, только если все тесты будут в одном классе.
sblundy

Это настраивает только индивидуальный набор тестов, а не весь этап тестирования
Бен

3

Мы тут

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

2

Что касается «Примечание: мы используем maven 2 для нашей сборки. Я пробовал использовать этапы тестирования maven до и после интеграции, но, если тест не проходит, maven останавливается и не запускает тест после интеграции. , что бесполезно ".

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


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

2

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

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?


1
Это не так, как показывают мои собственные тесты. У меня есть суперкласс, который запускает встроенную стеклянную рыбку в beforeclass и выключает ее после класса. У меня есть 2 класса, которые расширяют этот суперкласс. Класс before выполняется перед запуском тестов, определенных в каждом классе.
Джонатан Моралес Велес

0

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


0

Поскольку maven-surefire-plugin не запускает сначала класс Suite, а обрабатывает классы набора и тестирования одинаково, поэтому мы можем настроить плагин, как показано ниже, чтобы включить только классы набора и отключить все тесты. 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>

0

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

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, поэтому я не уверен, насколько это переносимо за пределами этой среды


3
Это пример для JUnit3, и OP запросил JUnit4, но на всякий случай некоторые пользователи JUnit3 найдут этот вопрос ... Для JUnit3 было бы лучше избавиться от метода main () и иметь метод suite () оберните TestSuite в подкласс junit.extensions.TestSetup. У вас все еще есть те же предостережения, что и в примере Джули, о запуске отдельных тестовых классов в вашей среде IDE.
NamshubWriter 05

0

Если вы не хотите создавать набор и должны перечислять все свои тестовые классы, вы можете использовать отражение, чтобы найти количество тестовых классов динамически, и обратный отсчет в базовом классе @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-ответа https://stackoverflow.com/a/37488620/5930242

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.