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

时间:2018-03-12 14:58:15

标签: java spring junit spring-test junit5

我正在使用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}''") SpringClassRuleSpringMethodRule是根据其API设计的。

1 个答案:

答案 0 :(得分:5)

JUnit 4

您所描述的方案实际上涵盖在SPR-15927

如果通过规则(例如,通过TestRule和Spring配置Spring,那么由Spring注入的自定义SpringClassRule实际上被JUnit选为规则是不可能的。 SpringMethodRule)。

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

换句话说,当Spring规则用于执行依赖注入时,当前Runner将不会注意到注入的自定义TestRule存在,因为该字段之前是{{1}在规则检测阶段。

这基本上是一个“鸡蛋和鸡蛋”问题,而且JUnit 4没有内置的解决方法。

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

JUnit Jupiter(JUnit 5)

使用JUnit Jupiter 5.1实际上应该更容易实现。也就是说,您可以将参数化测试支持与Spring结合起来,没有任何问题。

使用JUnit Jupiter的技巧是确保在类级别注册null(例如,通过SpringExtension@ExtendWith或类似名称)。然后,您可以在字段上使用@SpringJUnitConfig@RegisterExtension将Spring管理的扩展注入到JUnit Jupiter使用的测试实例中。