Как правильно настроить @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.