5

I wrote an ApplicationListener that should check if the environment is prepared during context initialization. I'm having trouble testing the scenario since I'm adding the listener manually both in my configure() and main() methods.

ApplicationListener class:

public class EnvironmentPrepared implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

        @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
            //code that checks if conditions are met

            if (checkTrue) {
            throw new RuntimeException();
        }
    }
}

Main class:

    public class MyApp extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        setRegisterErrorPageFilter(false);
        return application.listeners(new EnvironmentPrepared()).sources(MyApp.class);
    }

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(MyApp.class);
        springApplication.addListeners(new EnvironmentPrepared());
        springApplication.run(args);
    }
}

The test I want to execute:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(loader = OverriddenProfilesTest.CustomLoader.class)
public class OverriddenProfilesTest {

    public static class CustomLoader extends SpringBootContextLoader {

        @Override
        protected SpringApplication getSpringApplication() {
            SpringApplication app = super.getSpringApplication();
            app.addListeners(new EnvironmentPrepared());
            return app;
        }
    }

    /**
     * Checks if spring can bootstrap everything 
     */
    @Test(expected = RuntimeException.class)
    public void test() {

    }
}

This would be the test I want. A RuntimeException is thrown but the exception happens during context initialization so the test doesn't even start.

Marko Previsic
  • 1,820
  • 16
  • 30
perorororo
  • 191
  • 1
  • 14
  • you can try to load it programmatically inside your test method, check the hierarchy of `AbstractApplicationContext`, for xml configuration for example something like: `ApplicationContext applicationContext = new ClassPathXmlApplicationContext("my-app-context.xml");` – Paizo Jun 03 '19 at 14:51
  • Thank you for the input, I have tried it with the AbstractRefreshableApplicationContext but it didn't work how I expected. Seems to me that generally creating another context creates more problems. You can check out the solution I used beneath... – perorororo Jun 06 '19 at 13:09

1 Answers1

3

Here is the solution I used. I removed the manual adding of the listener to the application and used spring.factories file instead.

Regarding the test, I first created a custom runner class:

    public class SpringRunnerWithExpectedExceptionRule extends SpringJUnit4ClassRunner {

public SpringRunnerWithExpectedExceptionRule(Class<?> clazz) throws InitializationError {
    super(clazz);
}

@Override
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
    List<ExpectedException> testRules = getTestClass().getAnnotatedFieldValues(null, ExpectedExceptionClassRule.class, ExpectedException.class);
    Statement result = super.methodBlock(frameworkMethod);
    for (TestRule item : testRules) {
        result = item.apply(result, getDescription());
    }
    return result;
}}

Then I create following annotation:

@Retention(RUNTIME)
@Target({ FIELD })
public @interface ExpectedExceptionClassRule {

}

And finally, I was able to run the test with my runner:

@RunWith(SpringRunnerWithExpectedExceptionRule.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class OverriddenProfilesTest {

    @ExpectedExceptionClassRule
    public static ExpectedException expectedException = ExpectedException.none();

    @BeforeClass
    public static void before() {
        expectedException.expectCause(runtimeExceptionMethod());
    }


    @Test
    public void testThatShouldThrowExceptionWhileSettingContext {
    }

    static Matcher<Throwable> runtimeExceptionMethod() {
        return new IsRuntimeException();
    }

    static class IsRuntimeException extends TypeSafeMatcher<Throwable> {
    //do stuff
    }

More on the solution can be found here.

Marko Previsic
  • 1,820
  • 16
  • 30
perorororo
  • 191
  • 1
  • 14