До і після виконання пакета Suite у jUnit 4.x


84

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

Примітка: ми використовуємо maven 2 для нашої збірки. Я спробував використовувати maven's pre-& post-integration-testphases, але, якщо тест не вдається, maven зупиняється і не запускається post-integration-test, що не допомагає.


1
Для інтеграційних тестів вам слід використовувати плагін maven-failsafe-plugin замість surefire. Це не пропустить, post-integration-testякщо тест не пройде . Дивіться також цю вікі-сторінку .
Chris H.

Ви можете поділитися своїм остаточним впровадженням, будь ласка?
vikramvi

Відповіді:


113

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

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 і створив список включень, який вказував на набір, який я хотів запустити.
Джеріко

2
Станом на JUnit 4.8.2, це не відповідає параметризованим тестам. Методи @BeforeClass Suite будуть запущені після методу @ Parameterized.Parameters тесту, запобігаючи будь-якій залежності від налаштування Suite.
Anm

У відповідь на себе, коли я використовую @Theories, виклик методу @DataPoints є викликом після @BeforeClass Suite.
Anm

18
Вибачте за некро, але додавання BeforeClass / AfterClass до суперкласу не працює належним чином - вони все одно викликаються після завершення кожного тестового класу. Це для нащадків.
Subu Sankara Subramanian

3
Чи це все ще дійсний підхід? Як уникнути необхідності перераховувати список тестових класів в анотації SuiteClasses?
Бурхан Алі

33

Мій колега запропонував наступне: ви можете використовувати власний 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 Це перше корисне рішення, яке я бачив без громіздкого обслуговування класу Suites!
Stefan Haberl


7

Використовуючи анотації, ви можете зробити щось подібне:

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 зупиняється і не запускає тест після інтеграції , що не допомагає ".

замість цього ви можете спробувати плагін failsafe, я думаю, він має можливість забезпечити очищення незалежно від налаштування або стану проміжного етапу


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

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

Майте на увазі, що це рішення має масу недоліків:

  • повинен виконати всі тести пакета
  • повинен підклас класу "технічний"
  • ви не можете використовувати @BeforeClass та @AfterClass всередині підкласів
  • якщо ви виконали лише один тест в упаковці, очищення не проводиться
  • ...

Довідково: listClassesIn () => Як знайти всі підкласи даного класу в Java?


1
це не відповідає дійсності, наскільки показують мої власні тести. У мене є супер клас, який запускає вбудовану скляну рибку до попереднього класу і вимикає її після уроку. У мене тоді є 2 класи, які поширюються на цей супер клас. Клас beforeclass виконується перед запуском тестів, визначених у кожному класі.
Джонатан Моралес Велес,

0

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


0

Оскільки 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

Тоді єдиним способом, я думаю, отримати потрібну вам функціональність було б зробити щось подібне

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

Я використовую щось подібне в затемненні, тому не впевнений, наскільки це портативно поза цим середовищем


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

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.