@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)                // <-/

person Dominik Sandjaja    schedule 25.01.2010    source источник


Ответы (16)


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

abstract class A {
    void doInitialization() {}
}

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

    @Test
    void doTests() {}
}

Похоже, в TestNG есть @BeforeClass(dependsOnMethods={"doInitialization"}) - дайте ему пытаться.

person Bozho    schedule 25.01.2010
comment
В основном это то, чего я хотел избежать: не нужно явно вызывать методы супер (абстрактного) класса. Тем более, что у меня есть классы, которые наследуются от A, но не имеют собственного метода @BeforeClass. Мне пришлось бы вставить один только для этой цели. - person Dominik Sandjaja; 25.01.2010
comment
dependsOnMethods обходной путь помог. Хотя я бы предпочел подход сначала суперкласса ... - person Dominik Sandjaja; 25.01.2010
comment
Для использования зависимого метода не следует делать инициализацию аннотацией @Test? Это проблема, поскольку технически это не тест сам по себе ... - person N3da; 10.10.2014
comment
@BeforeClass должен аннотировать статический метод - person Fabrizio Stellato; 21.02.2019

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

Согласно JUnit api: "Методы суперклассов @BeforeClass будет запущен перед текущим классом ".

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

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

person Fortega    schedule 25.01.2010
comment
Несмотря на то, что исходный вопрос был для TestNG, я прибыл сюда после поиска в Google для JUnit, и ваш ответ помог - спасибо! - person teabot; 28.01.2011
comment
для JUnit вам необходимо, чтобы два метода назывались по-разному, хотя в противном случае будет запущен только метод подкласса, потому что родительский элемент будет затенен. - person Odys; 30.12.2016
comment
@Odys, большое спасибо за то, что упомянули об этом. Я изо всех сил пытался понять, почему метод установки в моем подклассе работал, а метод в его суперклассе - нет. Вы только что избавили меня от обиды! - person Tom Catullo; 17.08.2017
comment
Вы сделали мой день. Спасибо! - person raiks; 28.02.2020

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

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

class B extends A {    
 @Test
 doTests() {...}
}
person Paul    schedule 31.03.2011
comment
Это правда, но к делу не относится. Это не работает, когда класс B также имеет @BeforeClass-аннотированный метод, как в случае OP. - person jpaugh; 24.12.2015
comment
Я сделал то же самое. Кажется, что порядок наследования отсутствует, если базовый метод является частным. Спасибо! - person Manu; 02.06.2016

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

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

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

- Седрик

person Cedric Beust    schedule 26.01.2010
comment
Нет .xml для testng определен (явно), он запускается из Eclipse и Maven. - person Dominik Sandjaja; 26.01.2010
comment
Как именно вы его запускаете из Eclipse? Щелкнув правой кнопкой мыши по классу B? - person Cedric Beust; 26.01.2010

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

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}
person Konrad Garus    schedule 04.12.2013

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

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

person Anatolii Stepaniuk    schedule 04.08.2016

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

Похоже, это ошибка Eclipse или, по крайней мере, неожиданные результаты.

person Steve    schedule 17.11.2011

Как насчет того, чтобы ваш метод @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
}
person Tatome    schedule 09.07.2010
comment
BeforeClass должен быть статическим, поэтому вы не можете сделать это с помощью junit - person madx; 18.05.2016

dependsOnMethod можно использовать.

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

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")
person Sandeep Jindal    schedule 21.07.2015

Проверьте свой отчет об импорте. Должен быть

import org.testng.annotations.BeforeClass;

нет

import org.junit.BeforeClass;

person nishantrevo    schedule 03.02.2018

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

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

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

       //do class specific init

    }   

    @Test
    doTests() {...}
}
person srikar    schedule 25.06.2019
comment
Добавьте краткое объяснение того, что делает этот код, и ответьте на исходный вопрос. - person Cray; 25.06.2019

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

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

person Ludo    schedule 04.09.2013
comment
Честно говоря, даже логика могла измениться за последние 3,5 года с тех пор, как я задал этот вопрос ... ;-) Так что да, может быть, это была идея, а может, она не сработала - честно говоря, я не Помните. - person Dominik Sandjaja; 05.09.2013

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

Моя конкретная ситуация заключается в том, что мне нужно внедрить фиктивные службы из «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
    }
};

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

person vikingsteve    schedule 22.03.2017

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

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

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

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

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

Оказалось, что метод @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 ()
person rodikno    schedule 08.04.2019

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

person Mike Sokolov    schedule 13.03.2013

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

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

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

    @Test
    void doTests() {}
}
person kushal    schedule 23.05.2016
comment
Если вам нужно сначала выполнить метод в родительском классе, вам просто нужно назвать метод в дочернем классе иначе, чем у родительского (потому что, если они имеют одинаковую сигнатуру, то полиморфизм вступает в действие). Я считаю, что это более чистый способ. - person Anatolii Stepaniuk; 04.08.2016