it-swarm-ru.tech

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

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

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

81
sblundy

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

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 .

109
Julie

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

31
Martin Vysny
11
ArtB

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

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

    }

}
7
Jroc

Мы тут

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

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

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?

2
christophe blin

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

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

2
Binod Pant

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

0
FredBoutin

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

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

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

0
Jroc

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

0
user15299