How configure correctly @RunWith(Parameterized.class) + SpringClassRule + SpringMethodRule with a custom @Rule?

谁说胖子不能爱 提交于 2020-01-22 16:38:07

问题


I am working with Spring Framework 4.3.x and JUnit 4, I have the following structure

@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();

Thus the combination of:

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

works how is expected.

I have created a custom TestRule through ExternalResource as follows:

@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 {
      ...
    }

Thus If I try then use:

@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();

The CompleteRule is always ignored, it means the ExternalResource.before method overridden by CompleteRule is never executed.

I have tried use

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

And does not work. Even worst is not possible add SpringMethodRule because it implements MethodRule and not TestRule how the around method asks for.

I want avoid use hierarchy and work around with Rules instead. It because is a best practice.

Thus: Is there an approach to work around this?

Note I found in other post how a suggestion create @Rule and nest other Rules. Sadly no samples about this approach to test it.

Note is very important work around @RunWith(Parameterized.class) because is mandatory use @Parameters(name="{index}: ''{0}''") and SpringClassRule and SpringMethodRule are designed for this according with their API.

  • SpringClassRule
  • SpringMethodRule

回答1:


JUnit 4

The scenario you are describing is actually covered in SPR-15927.

It is impossible to have a custom TestRule injected by Spring actually picked up as a rule by JUnit if Spring is also configured via rules (e.g., via SpringClassRule and SpringMethodRule).

The custom TestRule field will in fact be injected by Spring, but that's too late in the game.

In other words, by the time the Spring rules are used to perform dependency injection, the current Runner will not notice that the injected custom TestRule exists, since the field was previously null during the rule detection phase.

It's basically a "chicken and egg" problem, and there is no built-in workaround for JUnit 4.

However, you can achieve something similar by asking Spring to perform dependency injection on an existing rule, but that requires custom code. See SPR-10252 for details.

JUnit Jupiter (JUnit 5)

With JUnit Jupiter 5.1 this should actually be easier to accomplish. Namely, you can combine parameterized test support with Spring without any problems.

The trick with JUnit Jupiter is to make sure you register the SpringExtension at the class level (e.g., via @ExtendWith, @SpringJUnitConfig, or similar). Then you can use @RegisterExtension and @Autowired on a field to have a Spring-managed extension injected into the test instance and used by JUnit Jupiter.



来源:https://stackoverflow.com/questions/49238290/how-configure-correctly-runwithparameterized-class-springclassrule-spring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!