@BeforeClass и наследование - порядок исполнения

У меня есть базовый базовый класс, который я использую в качестве основы для своих модульных тестов (TestNG 5.10). В этом классе я инициализирую всю среду для своих тестов, настраивая сопоставления баз данных и т.д. Этот абстрактный класс имеет метод с аннотацией @BeforeClass, который выполняет инициализацию.

Затем я расширяю этот класс с помощью определенных классов, в которых у меня есть методы @Test, а также методы @BeforeClass. Эти методы выполняют инициализацию среды, специфичную для класса (например, помещают некоторые записи в базу данных).

Как я могу обеспечить выполнение определенного порядка аннотированных методов @BeforeClass? Мне нужны те из абстрактного базового класса, который должен быть выполнен до тех, что распространяются в классе.

Пример:

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @BeforeClass
    doSpecificInitialization() {...}

    @Test
    doTests() {...}
}

Ожидаемый заказ:

A.doInitialization
B.doSpecificInitialization
B.doTests

Фактический порядок:

B.doSpecificInitialization // <- crashes, as the base init is missing
(A.doInitialization        // <---not executed
 B.doTests)                // <-/

Ответ 1

Не помещайте @BeforeClass в класс abstract. Вызовите его из каждого подкласса.

abstract class A {
    void doInitialization() {}
}

class B extends A {
    @BeforeClass
    void doSpecificInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

Кажется, что TestNG имеет @BeforeClass(dependsOnMethods={"doInitialization"}) - попробуйте.

Ответ 2

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

В соответствии с JUnit api: "Методы @BeforeClass суперклассов будут выполняться до тех, что относятся к текущему классу".

Я тестировал это, и, похоже, он работает для меня.

Однако, как упоминает @Odys ниже, для JUnit вам нужно иметь два метода по-разному, но, как и в противном случае, будет выполняться только метод подкласса, потому что родитель будет затенен.

Ответ 3

Я добавил public в абстрактный класс, а TestNG (6.0.1) выполнил doInitialization() до doTests. TestNG не выполняет doInitialization(), если я удаляю public из класса A.

public abstract class A {
 @BeforeClass
 doInitialization() {...}
}

class B extends A {    
 @Test
 doTests() {...}
}

Ответ 4

Я просто попробовал ваш пример с 5.11, и я сначала вызываю @BeforeClass из базового класса.

Можете ли вы разместить файл testng.xml? Возможно, вы указываете как A, так и B там, в то время как требуется только B.

Не стесняйтесь следить за списком рассылки testng-users, и мы можем более подробно рассмотреть вашу проблему.

- Седрик

Ответ 5

Я только что прошел через это и нашел еще один способ добиться этого. Просто используйте alwaysRun в @BeforeClass или @BeforeMethod в абстрактном классе, как и следовало ожидать.

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}

Ответ 6

Когда я запускаю из: JUnitCore.runClasses(TestClass.class); Он правильно выполнит родительский контроль перед ребенком (вам не нужно super.SetUpBeforeClass();). Если вы запустите его из Eclipse: По какой-то причине он не может запустить базовый класс. Работа вокруг: Вызовите базовый класс явно: (BaseTest.setUpBeforeClass();)  Возможно, вы захотите иметь флаг в базовом классе, если вы запустили его из приложения, чтобы определить, уже ли он настроен или нет. Поэтому он запускается только один раз, если вы запускаете его с помощью обоих возможных методов (например, от eclipse для личного тестирования и через ANT для выпуска сборки).

Это, кажется, ошибка с Eclipse или, по крайней мере, неожиданные результаты.

Ответ 7

Для JUnit: Как сказал @fortega: Согласно JUnit api: "Методы @BeforeClass суперклассов будут выполняться до тех, что относятся к текущему классу".

Но будьте осторожны , чтобы не называть оба метода с тем же именем. Поскольку в этом случае родительский метод будет скрыт дочерним родителем. Источник.

Ответ 8

Как насчет того, чтобы ваш метод @BeforeClass вызывал пустой метод specificBeforeClass(), который может или не может быть перезаписан подобными подклассами:

public class AbstractTestClass {
  @BeforeClass
  public void generalBeforeClass() {
    // do stuff
    specificBeforeClass();
  }

  protected void specificBeforeClass() {}
}

public class SpecificTest {
  @Override
  protected void specificBeforeClass() {
    // Do specific stuff
  }

  // Tests
}

Ответ 9

dependsOnMethod.

например. в случае Spring (AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")

Ответ 10

Проверьте свое выражение на импорт. Так должно быть

import org.testng.annotations.BeforeClass;

не

import org.junit.BeforeClass;

Ответ 11

Почему бы вам не попытаться создать абстрактный метод doSpecialInit() в вашем суперклассе, вызванный из аннотированного метода BeforeClass в суперклассе.

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

Ответ 12

Здесь есть еще одно простое решение.

Моя особая ситуация заключается в том, что мне нужно вводить макеты с "BeforeClass" в подкласс до того, как выполняется "BeforeClass" в суперклассе.

Для этого просто используйте @ClassRule в подклассе.

Например:

@ClassRule
public static ExternalResource mocksInjector = new ExternalResource() {
    @Override
    protected void before() {
        // inject my mock services here
        // Note: this is executed before the parent class @BeforeClass
    }
};

Надеюсь, это поможет. Это может эффективно выполнить статическую настройку в обратном порядке.

Ответ 13

Я столкнулся с подобной проблемой сегодня, единственное отличие было в том, что базовый класс не был

Вот мой случай

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    private void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

@BeforeClass метод @BeforeClass из класса A никогда не выполнялся.

  • A.doInitialization() → ЭТО БЫЛО НИКОГДА НЕ ВЫПОЛНЕНО
  • B.doSpecificInitialization()
  • B.doTests()

Играя с модификаторами конфиденциальности, я обнаружил, что TestNG не будет выполнять аннотированный метод @BeforeClass из унаследованного класса, если метод не виден из класса-наследника

Так что это будет работать:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

В результате происходит следующее:

  • A.doInitialization()
  • B.doSpecificInitialization()
  • B.doTests()

Ответ 14

Это работает для меня -

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}

Ответ 15

В моем случае (JUnit) у меня есть те же методы, что и setup() в базовом классе и производном классе. В этом случае вызывается только метод производного класса, и я вызываю метод базового класса.

Ответ 16

Лучшим и более чистым способом достижения этого с помощью наследования может быть следующее:

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

    @Override
    @BeforeClass
    void doInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}