Как правильно настроить @RunWith(Parameterized.class) + SpringClassRule + SpringMethodRule с пользовательским @Rule?

Я работаю с Spring Framework 4.3.x и JUnit 4, у меня есть следующая структура

@Transactional
@WebAppConfiguration
@RunWith(Parameterized.class)
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
@TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class CompleteTest {

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

Таким образом, сочетание:

  • @RunWith(Parameterized.class) + SpringClassRule + SpringMethodRule

работает так, как ожидается.

Я создал кастом TestRule через ExternalResource следующее:

@Component
public class CompleteRule extends ExternalResource {

    private static final Logger logger = LoggerFactory.getLogger(CompleteRule.class.getSimpleName());

    private final WebApplicationContext webApplicationContext;

    private final Environment environment;

    private MockMvc mockMvc;

    public CompleteRule(WebApplicationContext webApplicationContext, Environment environment) {
        this.webApplicationContext = webApplicationContext;
        this.environment = environment;
    }

    @Override
    protected void before() throws Throwable {
      ...
    }

Таким образом, если я попытаюсь использовать:

@Transactional
@WebAppConfiguration
@RunWith(Parameterized.class)
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
@TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class CompleteTest {

    private static final Logger logger = LoggerFactory.getLogger(CompleteTest.class.getSimpleName());

    @Rule
    @Autowired
    public CompleteRule completeRule;

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

CompleteRule всегда игнорируется, это означает, что ExternalResource.before метод переопределен CompleteRule никогда не выполняется.

Я пробовал использовать

@Rule
public TestRule chain = RuleChain.outerRule(SPRING_CLASS_RULE)
                                                .around(completeRule);

И не работает. Даже худшее не возможно добавить SpringMethodRule потому что это реализует MethodRule и не TestRule как around метод просит.

Я хочу избежать использования hierarchy и работать с Rules вместо. Это потому, что это лучшая практика.

Таким образом: есть ли подход, чтобы обойти это?

Обратите внимание, я нашел в другом посте, как создать предложение @Rule и вложите другие правила. К сожалению, нет примеров об этом подходе, чтобы проверить это.

Примечание очень важная работа вокруг @RunWith(Parameterized.class) потому что это обязательное использование @Parameters(name="{index}: ''{0}''")а также SpringClassRule а также SpringMethodRule разработаны для этого в соответствии с их API.

1 ответ

Решение

Юнит 4

Сценарий, который вы описываете, фактически описан в SPR-15927.

Невозможно иметь обычай TestRule Внедренный Spring на самом деле, как правило, выбирается JUnit, если Spring также настраивается через правила (например, через SpringClassRule а также SpringMethodRule).

Обычай TestRule Поле будет фактически введено весной, но это слишком поздно в игре.

Другими словами, к тому времени, когда правила Spring используются для внедрения зависимости, текущая Runner не заметит, что введенный кастом TestRule существует, так как поле было ранее null на этапе обнаружения правил.

По сути, это проблема "курицы и яйца", и для JUnit 4 нет встроенного обходного пути.

Однако вы можете добиться чего-то похожего, попросив Spring выполнить внедрение зависимостей в существующее правило, но для этого требуется специальный код. См. SPR-10252 для деталей.

Юнит Юпитер (Юнит 5)

С JUnit Jupiter 5.1 это на самом деле должно быть проще. А именно, вы можете без проблем объединить параметризованную поддержку тестирования с Spring.

Хитрость с JUnit Jupiter заключается в том, чтобы вы зарегистрировали SpringExtension на уровне класса (например, через @ExtendWith, @SpringJUnitConfig, или похожие). Тогда вы можете использовать @RegisterExtension а также @Autowired в поле для добавления расширения, управляемого Spring, в экземпляр теста и используемого JUnit Jupiter.

Другие вопросы по тегам