Тестирование перезапуска приложения с сохранением с помощью JUnit и Spring - PullRequest
0 голосов
/ 12 мая 2018

У меня есть приложение, которое использует постоянные очереди JMS.Представьте, что у меня сбой приложения после прочтения сообщения и до его подтверждения.Постоянная очередь должна предоставить это сообщение снова после перезапуска приложения.Как я могу реализовать интеграционный тест junit для этого?Я тестирую перезапуск приложения после (смоделированного) сбоя приложения в середине «транзакции».

Я рассматривал @DirtiesContext как способ сброса всех частей Spring приложения: чтение конфигов, воссозданиеJMS-соединения.Я мог бы иметь один контрольный пример A) написать сообщение, разрешить чтение сообщения и затем «выйти» (закрыть контекст весны?) Без подтверждения.Затем другой контрольный пример (после перезагрузки контекста B) считывает сообщение и утверждает, что оно не было потеряно после перезапуска смоделированного приложения.Но встроенная перезагрузка контекста, предоставляемая @DirtiesContext, происходит только между тестовыми примерами.И JUnit не предоставляет средства для последовательности двух тестовых случаев или зависимости B) от A), так что A) всегда будет работать (и запускаться первым), если вы решите запустить B).

ВВ прошлой жизни я написал ручной код, который закрывал весенний контекст и вручную перезапустил новый контекст.Например, между А) и Б).Это может быть сделано в одном тестовом случае.Полагаю, что с @RunWith(SpringRunner.class) это не будет хорошо играть, и кажется довольно старой школой.Это действительно единственный вариант, учитывая всю замечательную поддержку Spring и JUnit в наши дни?

Это кажется довольно полезной техникой.Его можно использовать для проверки повторного поступления сообщений после их отката (и они застряли в очереди недоставленных сообщений);или что порядковые номера, записанные в БД, действительно сохраняются во время «сбоя».Любое количество случаев сбоев, которые могут повлиять на следующий запуск приложения из-за постоянных (или нет) данных.Как мы имитируем весенний перезапуск в тестах junit?Либо в рамках одного теста, либо создайте последовательность зависимых тестов с @DirtiesContext между ними.

1 Ответ

0 голосов
/ 23 мая 2019

Следующая статья Как перезапустить контекст приложения Spring в тесте JUnit описывает решение вопроса.


Решение состоит в том, чтобы потратить SpringJUnit4ClassRunner, чтобы добавить SpringRestarter синглтон, который заботится о восстановлении контекста приложения.

public class SpringRestarter {

    private static SpringRestarter INSTANCE = null;

    private TestContextManager testContextManager;

    public static SpringRestarter getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SpringRestarter();
        }

        return INSTANCE;
    }

    public void init(TestContextManager testContextManager) {
        this.testContextManager = testContextManager;
    }

    public void restart(Runnable stoppedLogic) {
        testContextManager.getTestContext().markApplicationContextDirty(DirtiesContext.HierarchyMode.EXHAUSTIVE);

        if (stoppedLogic != null) {
            stoppedLogic.run();
        }

        testContextManager.getTestContext().getApplicationContext();
        reinjectDependencies();
    }

    private void reinjectDependencies()  {
        testContextManager
                .getTestExecutionListeners()
                .stream()
                .filter(listener -> listener instanceof DependencyInjectionTestExecutionListener)
                .findFirst()
                .ifPresent(listener -> {
                    try {
                        listener.prepareTestInstance(testContextManager.getTestContext());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
    }
}

RestartingSpringJUnit4ClassRunner расширяет SpringJUnit4ClassRunner и инициализирует синглтон SpringRestarter.

public class RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {

    public RestartingSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected TestContextManager createTestContextManager(Class<?> clazz) {

        final TestContextManager testContextManager = super.createTestContextManager(clazz);

        SpringRestarter.getInstance().init(testContextManager);
        return testContextManager;
    }
}

RestartingSpringRunner расширяет RestartingSpringJUnit4ClassRunner, чтобы расширить SpringRunner

public class RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner {

    public RestartingSpringRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }
}

Использование в тесте JUnit 4

@RunWith(RestartingSpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIntegrationTests {

    public void myRestartingTest() {
        //Some test logic before the context restart

        SpringRestarter.getInstance().restart(() -> {/* Some logic after context stopped */});

        //Some test logic after the context restart
    }
}

Полные детали объяснения

...