如何使用自定义@Rule 正确配置@RunWith(Parameterized.class) + SpringClassRule + SpringMethodRule?

How configure correctly @RunWith(Parameterized.class) + SpringClassRule + SpringMethodRule with a custom @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();

因此组合:

按预期工作。

我通过 ExternalResource 创建了一个自定义 TestRule 如下:

@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方法被覆盖了 by CompleteRule 永远不会被执行。

我试过使用

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

而且不起作用。更糟糕的是不可能添加 SpringMethodRule 因为它实现了 MethodRule 而不是 TestRule 如何实现 around 方法的要求。

我想避免使用 hierarchy 并改用 Rules。这是因为这是最佳实践。

因此:是否有解决此问题的方法?

注意 我在其他 post 中找到了如何创建 @Rule 和嵌套其他规则的建议。遗憾的是没有关于这种方法的样本来测试它。

注意 是解决 @RunWith(Parameterized.class) 非常重要的工作,因为它是强制使用 @Parameters(name="{index}: ''{0}''") SpringClassRuleSpringMethodRule 是根据他们的 API.

设计的

JUnit 4

您所描述的场景实际上包含在SPR-15927

不可能让 Spring 注入的自定义 TestRule 实际上被 JUnit 拾取为 rule 如果 Spring 也是通过规则配置(例如,通过 SpringClassRuleSpringMethodRule)。

自定义 TestRule 字段实际上将由 Spring 注入,但在游戏中为时已晚。

换句话说,当 Spring 规则被用来执行依赖注入时,当前 Runner 将不会注意到注入的自定义 TestRule 存在,因为字段之前在规则检测阶段null

这基本上是一个 "chicken and egg" 问题,JUnit 4 没有 built-in 解决方法。

但是,您可以通过 请求 Spring 对现有规则执行依赖项注入来实现类似的功能,但这需要自定义代码。有关详细信息,请参阅 SPR-10252

JUnit 木星 (JUnit 5)

有了 JUnit Jupiter 5.1,这实际上应该更容易实现。也就是说,您可以毫无问题地将参数化测试支持与 Spring 结合使用。

JUnit Jupiter 的诀窍是确保您在 class 级别注册 SpringExtension(例如,通过 @ExtendWith@SpringJUnitConfig 或类似方式)。然后,您可以在字段上使用 @RegisterExtension@Autowired 将 Spring-managed 扩展注入到 JUnit Jupiter 使用的测试实例 中。