Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-7731

Provide @Rule alternative to SpringJUnit4ClassRunner

    Details

    • Type: New Feature
    • Status: Closed
    • Priority: Major
    • Resolution: Complete
    • Affects Version/s: None
    • Fix Version/s: 4.2 RC1
    • Component/s: Test
    • Last commented by a User:
      false

      Description

      Overview

      Sometimes (always?) it would be nice to have a @Rule implementation that did the job of the SpringJUnit4ClassRunner.

      The main motivation for this is to be able to use other runners (e.g. @Parameterized, @Categories, Mockito, etc.).

      There might even be a case for deprecating the runner in favor of a @Rule?


      Further Resources

      1. SpringContextRule.java
        5 kB
        Neale Upstone
      2. SpringContextRule.java
        4 kB
        Neale Upstone

        Issue Links

          Activity

          Hide
          sbrannen Sam Brannen added a comment -

          Implemented as described in GitHub commit d1b1c4f:

          Introduce JUnit Rule alternative to SpringJUnit4ClassRunner

          Since Spring Framework 2.5, support for integrating the Spring TestContext Framework (TCF) into JUnit 4 based tests has been provided via the SpringJUnit4ClassRunner, but this approach precludes the ability for tests to be run with alternative runners like JUnit's Parameterized or third-party runners such as the MockitoJUnitRunner.

          This commit remedies this situation by introducing @ClassRule and @Rule based alternatives to the SpringJUnit4ClassRunner. These rules are independent of any Runner and can therefore be combined with alternative runners.

          Due to the limitations of JUnit's implementation of rules, as of JUnit 4.12 it is currently impossible to create a single rule that can be applied both at the class level and at the method level (with access to the test instance). Consequently, this commit introduces the following two rules that must be used together.

          • SpringClassRule: a JUnit TestRule that provides the class-level functionality of the TCF to JUnit-based tests
          • SpringMethodRule: a JUnit MethodRule that provides the instance-level and method-level functionality of the TCF to JUnit-based tests

          In addition, this commit also introduces the following new JUnit Statements for use with rules:

          • RunPrepareTestInstanceCallbacks
          • ProfileValueChecker
          Show
          sbrannen Sam Brannen added a comment - Implemented as described in GitHub commit d1b1c4f : Introduce JUnit Rule alternative to SpringJUnit4ClassRunner Since Spring Framework 2.5, support for integrating the Spring TestContext Framework (TCF) into JUnit 4 based tests has been provided via the SpringJUnit4ClassRunner , but this approach precludes the ability for tests to be run with alternative runners like JUnit's Parameterized or third-party runners such as the MockitoJUnitRunner . This commit remedies this situation by introducing @ClassRule and @Rule based alternatives to the SpringJUnit4ClassRunner . These rules are independent of any Runner and can therefore be combined with alternative runners. Due to the limitations of JUnit's implementation of rules, as of JUnit 4.12 it is currently impossible to create a single rule that can be applied both at the class level and at the method level ( with access to the test instance). Consequently, this commit introduces the following two rules that must be used together. SpringClassRule : a JUnit TestRule that provides the class-level functionality of the TCF to JUnit-based tests SpringMethodRule : a JUnit MethodRule that provides the instance-level and method-level functionality of the TCF to JUnit-based tests In addition, this commit also introduces the following new JUnit Statements for use with rules: RunPrepareTestInstanceCallbacks ProfileValueChecker
          Hide
          sbrannen Sam Brannen added a comment -

          FYI: minor changes in latest commit:

          Introduce TestContextManager cache in SpringClassRule

          In order to simplify configuration of the SpringMethodRule and to ensure
          that the correct TestContextManager is always retrieved for the
          currently executing test class, this commit introduces a static
          TestContextManager cache in SpringClassRule.

          In addition, since it is not foreseen that SpringClassRule and
          SpringMethodRule should be able to be subclassed, their internal methods
          are now private instead of protected.

          Consequently, the required configuration now looks like this:

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

          The only change from the user's perspective is that this no longer has to be supplied to the SpringMethodRule constructor.

          Regards,

          Sam

          Show
          sbrannen Sam Brannen added a comment - FYI: minor changes in latest commit : Introduce TestContextManager cache in SpringClassRule In order to simplify configuration of the SpringMethodRule and to ensure that the correct TestContextManager is always retrieved for the currently executing test class, this commit introduces a static TestContextManager cache in SpringClassRule. In addition, since it is not foreseen that SpringClassRule and SpringMethodRule should be able to be subclassed, their internal methods are now private instead of protected. Consequently, the required configuration now looks like this: @ClassRule public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();   @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); The only change from the user's perspective is that this no longer has to be supplied to the SpringMethodRule constructor. Regards, Sam
          Hide
          thomasd Thomas Darimont added a comment -

          Just stumbled upon this issue, this is great stuff Sam!

          I just wondered whether it would be possible to extend this PoC to add support for specifying test case specific configuration classes which comes in handy If you want to be able to test different configurations with just minor differences.
          I gave this a quick spin a while ago: https://gist.github.com/thomasdarimont/9bf439fa3c48f110b49e

          One could specify an additional config class to use via (a new) @Use annotation on method level:

          @ContextConfiguration(loader = AnnotationConfigContextLoader.class)
          public class SpringJunit4RunnerRuleTests {
           
          	@ClassRule public static SpringContext springContext = new SpringContext();
           
          	@Autowired Foo foo;
           
          	@Test
          	public void test1_should_use_ConcreteFoo() {
           
          		Assert.assertNotNull(foo);
          		Assert.assertTrue(foo instanceof ConcreteFoo);
          	}
           
          	@Test
          	@Use(SpecialConfig.class)
          	public void test2_should_use_SpecialFoo() {
           
          		Assert.assertNotNull(foo);
          		Assert.assertTrue(foo instanceof SpecialFoo);
          	}
           
          	@Test
          	public void test3_should_use_ConcreteFoo() {
           
          		Assert.assertNotNull(foo);
          		Assert.assertTrue(foo instanceof ConcreteFoo);
          	}
           
          	static interface Foo {}
           
          	static class ConcreteFoo implements Foo {};
           
          	static class SpecialFoo implements Foo {}
           
          	@Configuration
          	static class Config {
           
          		@Bean
          		public Foo foo() {
          			return new ConcreteFoo();
          		}
          	}
           
          	// Not visible for the test directly
          	public static class Nested {
           
          		@Configuration
          		public static class SpecialConfig extends Config {
           
          			@Bean
          			public Foo foo() {
          				return new SpecialFoo();
          			}
          		}
          	}
          }

          Show
          thomasd Thomas Darimont added a comment - Just stumbled upon this issue, this is great stuff Sam! I just wondered whether it would be possible to extend this PoC to add support for specifying test case specific configuration classes which comes in handy If you want to be able to test different configurations with just minor differences. I gave this a quick spin a while ago: https://gist.github.com/thomasdarimont/9bf439fa3c48f110b49e One could specify an additional config class to use via (a new) @Use annotation on method level: @ContextConfiguration (loader = AnnotationConfigContextLoader. class ) public class SpringJunit4RunnerRuleTests { @ClassRule public static SpringContext springContext = new SpringContext(); @Autowired Foo foo; @Test public void test1_should_use_ConcreteFoo() { Assert.assertNotNull(foo); Assert.assertTrue(foo instanceof ConcreteFoo); } @Test @Use (SpecialConfig. class ) public void test2_should_use_SpecialFoo() { Assert.assertNotNull(foo); Assert.assertTrue(foo instanceof SpecialFoo); } @Test public void test3_should_use_ConcreteFoo() { Assert.assertNotNull(foo); Assert.assertTrue(foo instanceof ConcreteFoo); } static interface Foo {} static class ConcreteFoo implements Foo {}; static class SpecialFoo implements Foo {} @Configuration static class Config { @Bean public Foo foo() { return new ConcreteFoo(); } } // Not visible for the test directly public static class Nested { @Configuration public static class SpecialConfig extends Config { @Bean public Foo foo() { return new SpecialFoo(); } } } }
          Hide
          david_syer Dave Syer added a comment -

          I like that (I think I suggested using `@ContextConfiguration` at method level somewhere else). It would be really useful to be able to override the odd bean for a single method and not have to `@DirtiesContext` for the main, parent context.

          Show
          david_syer Dave Syer added a comment - I like that (I think I suggested using `@ContextConfiguration` at method level somewhere else). It would be really useful to be able to override the odd bean for a single method and not have to `@DirtiesContext` for the main, parent context.
          Hide
          sbrannen Sam Brannen added a comment -

          Thanks, Thomas!

          Glad you like the Rule support.

          Regarding your proposal, we already have something similar in SPR-12031. So could you please post your comments to that JIRA issue?

          Cheers,

          Sam

          Show
          sbrannen Sam Brannen added a comment - Thanks, Thomas! Glad you like the Rule support. Regarding your proposal, we already have something similar in SPR-12031 . So could you please post your comments to that JIRA issue? Cheers, Sam

            People

            • Assignee:
              sbrannen Sam Brannen
              Reporter:
              david_syer Dave Syer
              Last updater:
              Sam Brannen
            • Votes:
              25 Vote for this issue
              Watchers:
              30 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                8 weeks, 5 days ago