Повторне використання контексту весняного додатку в класах тестування junit


83

У нас є купа тестових кейсів JUnit (інтеграційні тести), і вони логічно згруповані в різні тестові класи.

Ми можемо завантажувати контекст програми Spring один раз для класу тесту та повторно використовувати його для всіх тестових випадків у тестовому класі JUnit, як зазначено у http://static.springsource.org/spring/docs/current/spring-framework-reference /html/testing.html

Однак нам просто було цікаво, чи є спосіб завантажити контекст програми Spring лише один раз для групи тестових класів JUnit.

FWIW, ми використовуємо Spring 3.0.5, JUnit 4.5 і використовуємо Maven для побудови проекту.


5
Усі відповіді нижче чудові, але у мене немає context.xml. Я коментував свій шлях до забуття? Будь-який спосіб зробити це без context.xml?
markthegrea

2
ти знайшов відповідь на своє рішення? у мене така сама проблема, і я хочу зробити це за допомогою анотацій та Spring Boot.
AleksandarT

Відповіді:


96

Так, це цілком можливо. Все, що вам потрібно зробити, це використовувати той самий locationsатрибут у своїх тестових класах:

@ContextConfiguration(locations = "classpath:test-context.xml")

Spring кешує контексти програми за locationsатрибутом, тому, якщо те саме locationsз'являється вдруге, Spring використовує той самий контекст, а не створює новий.

Я написав статтю про цю функцію: Прискорення тестів інтеграції Spring . Також це детально описано у весняній документації: 9.3.2.1 Управління контекстом та кешування .

Це має цікавий підтекст. Оскільки Spring не знає, коли виконано JUnit, він назавжди кешує весь контекст і закриває їх за допомогою гачка вимкнення JVM. Така поведінка (особливо коли у вас багато тестових класів з різними locations) може призвести до надмірного використання пам'яті, витоків пам'яті тощо. Ще одна перевага контексту кешування.


Ах! Не усвідомлював цього. Ми слідуємо цьому підходу вже давно, і я (помилково) приписую тривалість виконання тесту пружинному завантаженню контексту з кожним класом тесту. Зараз ретельно перевіримо. Дякую.
Рамеш

1
Я б скоріше сказав, що весна не знає про порядок виконання ваших тестів. В результаті цього він не може сказати, чи потрібен контекст пізніше, або його можна утилізувати.
філнат

1
Я не розумію, як це насправді може бути правдою. Eclipse / JUnit витрачає 2 хвилини на викручування навколишнього середовища кожного разу, коли я роблю тест Run As / JUnit. Цього не сталося б, якби щось було кешовано.
user1944491 02

3
Будь-яка ідея, чи можна це зробити повністю за допомогою анотацій, замість використання XML для визначення контексту? Я багато шукав про це в документі та тут на SO, але не міг знайти нічого, що змушує мене думати, що це неможливо.
Жан-Франсуа Савард

чи це не відповідає дійсності, якщо у вас є ініціалізатори? мій ініціалізується для кожного тесту в класі
Kalpesh Soni

26

Щоб додати до відповіді Томаша Нуркевича , станом на весну 3.2.2 @ContextHierarchyможна використовувати анотацію, щоб мати окрему, пов’язану множинну структуру контексту. Це корисно, коли кілька тестових класів хочуть спільно використовувати (наприклад) налаштування бази даних в пам'яті (джерело даних, EntityManagerFactory, менеджер tx тощо).

Наприклад:

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("FirstTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstTest {
 ...
}

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("SecondTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondTest {
 ...
}

Завдяки цьому налаштуванню контекст, який використовує "test-db-setup-context.xml", буде створений лише один раз, але компоненти всередині нього можуть бути введені в контекст окремого модульного тесту

Детальніше про посібник: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (пошук за " ієрархією контексту ")


У мене багатомодульний maven, і я намагаюся уникати налаштування бази даних в сервісному модулі (оскільки він вже завантажений тестами модуля доступу до даних), і це не працює для мене!
Мухаммед Хеді

5
Це спрацювало для мене! Дякую. Щоб зрозуміти, без анотації @ContextHierarchy весна завантажує мою базу даних для кожного тесту. Я використовую параметр "classes": @ContextConfiguration (classes = {JpaConfigTest.class, ...
Brel,

5
Будь-яка ідея, чи можна це зробити повністю за допомогою анотацій замість використання XML для визначення контексту? Я багато шукав про це в документі та тут на SO, але не міг знайти нічого, що змушує мене думати, що це неможливо.
Жан-Франсуа Савард

1
@ Jean-FrançoisSavard Вам чи пощастило у ваших пошуках (за допомогою анотацій w замість XML)?
javadev

@javadev Я сподіваюся, це те, що ви шукаєте docs.spring.io/spring/docs/current/spring-framework-reference/…
Raviteja Gubba

1

В основному spring досить розумний, щоб налаштувати це для вас, якщо у вас однакова конфігурація контексту програми для різних класів тесту. Наприклад, скажімо, у вас є два класи A і B, як показано нижче:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

У цьому прикладі клас A висміює bean C, тоді як клас B висміює bean D. Отже, Spring розглядає їх як дві різні конфігурації і, отже, завантажує контекст програми один раз для класу A і один раз для класу B.

Якщо замість цього ми хотіли б, щоб spring поділився контекстом програми між цими двома класами, вони мали б виглядати приблизно так:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

Якщо ви підключите свої класи таким чином, spring завантажуватиме контекст програми лише один раз для класу A або B, залежно від того, який з двох класів запускається першим у наборі тестів. Це може бути відтворено в декількох тестових класах, лише критерієм є те, що ви не повинні налаштовувати тестові класи по-різному. Будь-яка настройка, в результаті якої тестовий клас відрізнятиметься від інших (на очах весни), в кінцевому підсумку створить інший контекст програми до весни.


0

створіть свій клас конфігурації, як показано нижче

@ActiveProfiles("local")
@RunWith(SpringJUnit4ClassRunner.class )
@SpringBootTest(classes ={add your spring beans configuration classess})
@TestPropertySource(properties = {"spring.config.location=classpath:application"})
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class);


    //auto wire all the beans you wanted to use in your test classes
    @Autowired
    public XYZ xyz;
    @Autowired
    public ABC abc;


    }



Create your test suite like below



@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class,test2.class})
public class TestSuite extends RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class);


}

Створіть свої тестові класи, як показано нижче

public class Test1 extends RunConfigration {


  @Test
    public void test1()
    {
    you can use autowired beans of RunConfigration classes here 
    }

}


public class Test2a extends RunConfigration {

     @Test
    public void test2()
    {
    you can use autowired beans of RunConfigration classes here 
    }


}

0

Один чудовий момент полягає в тому, що якщо ми використовуємо @SpringBootTests, але знову ж таки use @MockBean in different test classes, Spring не має можливості повторно використати контекст своєї програми для всіх тестів.

Рішення полягає в to move all @MockBean into an common abstract classтому, що вирішує проблему.

@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class)
public abstract class AbstractIT {

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

Тоді тестові класи можна побачити, як показано нижче

public class ProductControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchProduct_ShouldSuccess() {
   }

}

public class InvoiceControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchInvoice_ShouldSuccess() {
   }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.