12

In my application I initialize a property before spring application startup as follows:

MapLookup.setMainArguments(new String[] {"logging.profile", profile}); //from args
SpringApplication.run(source, args);

(just for reference: it is used for log4j2 logging, which must be set before spring starts to initialize).

Now I want to run an @IntegrationTest, but use the same logging configuration. Obviously I cannot use the code above, as a JUnit test is not executed using SpringApplication.run.

So, how could I initialize code before a @RunWith(SpringJUnit4ClassRunner.class) starts?

Note: BeforeClass does not work as this is executed after spring context startup.

membersound
  • 81,582
  • 193
  • 585
  • 1,120

2 Answers2

14

You can run the initialization in a static initializer. Static initializer will run after JUnit loads the test class and before JUnit reads any annotations on it.

Alternatively you can extend SpringJUnit4ClassRunner with your own Runner initialize in it first and then run SpringJUnit4ClassRunner

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 2
    You mean a `static { }` block in the test class? I just tried it, but without success. – membersound Mar 26 '15 at 09:27
  • 1
    I tried it too, it clearly runs before SpringJUnit4ClassRunner, JUnit cannot know about SpringJUnit4ClassRunner before it loads the test class and once it loads the class it will run static {} – Evgeniy Dorofeev Mar 26 '15 at 09:29
  • 1
    Hm, ok it runs before the ClassRunner, but in runs after `log4j` initialization... So I issume this *is* actually a log4j issue indeed? – membersound Mar 26 '15 at 09:30
  • log4j initialization runs only if some code uses it, possibly somewhere some code did it before you run the test – Evgeniy Dorofeev Mar 26 '15 at 09:33
  • 1
    I also tried moving the static block to an extended `SpringJUnit4ClassRunner`, but got the same result. It is strange that log4j actually runs before the ClassRunner, because when running from `static void main` the log4j config is first initialized on `SpringApplication.run`. – membersound Mar 26 '15 at 09:35
  • Probably it is caused by `private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class);` which forces the log4j initalization directly when running... Could I prevent this, or would I have to copy the whole class? – membersound Mar 26 '15 at 09:39
  • copy and change should help but the idea does not look nice – Evgeniy Dorofeev Mar 26 '15 at 09:47
  • I copied the class and moved the logger initalization inside the `static` codeblock, and put the `MapLookup` right before. That works, but indeed this is an ugly solution. The main problem is that any `private static` fields gets initialized before `static` codeblocks, thus I cannot set the `MapLookup` in any way before the `Logger` in `SpringJUnit4ClassRunner` is initialized. – membersound Mar 26 '15 at 09:49
  • I used some static methods in the Config class and so I had to mock those static methods for the JUnit test case. It was not working with @BeforeClass but worked fine with the static block. Your explanation makes sense. Thanks for the insight. – Uresh K Oct 22 '18 at 21:33
3

I had a slightly different problem. I need to deploy something to my service after the Spring context is loaded. Solution use a custom config class for the test and run the deployment within a @PostConstruct Method.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public class JunitTest {

  @Configuration
  @ComponentScan(basePackages = { "de.foo })
  public static class TestMConfig {

      @Autowired
      private DeploymentService service;


      @PostConstruct
      public void init() {
        service.deploy(...);
      }
  }

  @Test
  public void test() {
      ...
  }
}

Maybe this helps, someone, sometime, somewhere ;)

outofBounds
  • 594
  • 6
  • 18