Skip to content

Improve @PropertyMapping annotations can only be used on test classes message #5897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wilkinsona opened this issue May 9, 2016 · 2 comments
Milestone

Comments

@wilkinsona
Copy link
Member

This is a follow-on from #5882.

In trying to produce the idiomatic 1.4 equivalent of @dsyer's slightly unusual test I discovered that @PropertyMapping prevents a test class from also being annotation with @Component. For example, this will fall in a heap:

@RunWith(SpringRunner.class)
@WebMvcTest
@RestController
public class WebMvcTests {

    private static Log logger = LogFactory.getLog(WebMvcTests.class);

    @Autowired
    private MockMvc mockMvc;

    private static Object span;

    @RequestMapping("/ping")
    public String ping() {
        logger.info("ping");
        WebMvcTests.span = new Object();
        return "ping";
    }

    @RequestMapping("/future")
    public CompletableFuture<String> future() {
        logger.info("future");
        return CompletableFuture.completedFuture("ping");
    }

    @Test
    public void getCallsEndpoint() throws Exception {
        this.mockMvc.perform(get("/ping")).andDo(print());
        assertThat(WebMvcTests.span, is(notNullValue()));
    }

    @Test
    public void futureCreatesAsync() throws Exception {
        this.mockMvc.perform(get("/future")).andExpect(request().asyncStarted());
    }

}

The failure is:

java.lang.IllegalStateException: @PropertyMapping annotations can only be used on test classes
    at org.springframework.util.Assert.state(Assert.java:392)
    at org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer$PropertyMappingCheckBeanPostProcessor.postProcessBeforeInitialization(PropertyMappingContextCustomizer.java:80)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:399)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:119)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener.prepareTestInstance(AutoConfigureReportTestExecutionListener.java:46)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

The nearest equivalent in 1.4 that works is:

@RunWith(SpringRunner.class)
@WebMvcTest
public class WebMvcTests {

    private static Log logger = LogFactory.getLog(WebMvcTests.class);

    @Autowired
    private MockMvc mockMvc;

    private static Object span;

    @Test
    public void getCallsEndpoint() throws Exception {
        this.mockMvc.perform(get("/ping")).andDo(print());
        assertThat(WebMvcTests.span, is(notNullValue()));
    }

    @Test
    public void futureCreatesAsync() throws Exception {
        this.mockMvc.perform(get("/future")).andExpect(request().asyncStarted());
    }

    @TestConfiguration
    @RestController
    static class Foo {

        @RequestMapping("/ping")
        public String ping() {
            logger.info("ping");
            WebMvcTests.span = new Object();
            return "ping";
        }

        @RequestMapping("/future")
        public CompletableFuture<String> future() {
            logger.info("future");
            return CompletableFuture.completedFuture("ping");
        }

    }

}
@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label May 9, 2016
@philwebb
Copy link
Member

I think making the test itself a component is probably bad practice but we should certainly enhance the error message.

@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label May 12, 2016
@philwebb philwebb added this to the 1.4.0.M3 milestone May 12, 2016
@philwebb
Copy link
Member

A bit more digging. With the original test, two instances of WebMvcTests are created. The first is by JUnit and the second is by Spring. I think this is quite likely to cause other problems so we are best to still throw some kind of exception.

@philwebb philwebb changed the title PropertyMapping prevents a test class from also being a component Improve @PropertyMapping annotations can only be used on test classes message May 14, 2016
philwebb added a commit that referenced this issue May 14, 2016
Improve the message thrown when a @PropertyMapping is used in
combination with a @component to include the actual annotations that
are causing the problem.

Fixes gh-5897
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants