spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.71k stars 40.58k forks source link

Remove mention of @TestComponent from the documentation as it is an implementation detail of @TestConfiguration #8421

Closed michael-simons closed 7 years ago

michael-simons commented 7 years ago

Hi,

hopefully I'm not doing anything wrong here. I assume that @TestComponent should be picked up when running tests with @SpringBootTest without further ado, but neither this

package com.example;

import org.springframework.boot.test.context.TestComponent;

@TestComponent
public class ToplevelTestComponent {

}

together with

package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ToplevelTestComponentNotWorkingTest {
    @Autowired
    ToplevelTestComponent toplevelTestComponent;

    @Test
    public void f() {
    }
}

nor this (nested version)

package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestComponent;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class NestedTestComponentNotWorkingTest {
    @TestComponent
    static class NestedTestComponent{
    }

    @Autowired
    NestedTestComponent nestedTestComponent;

    @Test
    public void f() {
    }
}

works. Both fail with org.springframework.beans.factory.NoSuchBeanDefinitionException. Sample project is attached: InitializrSpringbootProject.zip Tested with 1.4.0, 1.4.3 and 1.5.1.

Maybe it's a documentation issue, but I don't see my error…

snicoll commented 7 years ago

Javadoc:

@Component that can be used when a bean is intended only for tests, and should be excluded from Spring Boot's component scanning.

Documentation:

To help prevent this, Spring Boot provides @TestComponent and @TestConfiguration annotations that can be used on classes in src/test/java to indicate that they should not be picked up by scanning.

What do you expect based on the doc?

michael-simons commented 7 years ago

Exactly the example above: Annotating a pojo in src/test/java with only @TestComponent with the result that it gets picked up during test, not that I have to use both @Component and @TestComponent.

michael-simons commented 7 years ago

If I have an @Component inside src/test/java, it get's picked up only during tests anyway, not during normal start. I cannot annotate any component inside src/main/java with @TestComponent having spring-boot-starter-test as test dependency.

snicoll commented 7 years ago

I am not following, the doc states that it will not be picked up by component scan. Why do you want it to be picked up all the sudden then? @TestConfiguration aren't picked up by component scan either ...

snicoll commented 7 years ago

Perhaps we could turn that one into a documentation enhancement. I am not excluding that @philwebb had something else in mind that I didn't get so it's certainly a good use of our time.

michael-simons commented 7 years ago

Sadly, you gave up when I just understood the purpose of @TestComponent.

I'd recommend you separate it a bit from @TestConfiguration in the docs and state that @TestComponent facilitate the creation of @TestConfiguration.

(TBH: I wonder if @TestComponent belongs into the reference at all or should only be part of JavaDoc)

Thanks, @snicoll for the good discussion. Got it now.

snicoll commented 7 years ago

The conversation on gitter was going in circles. If we've added that in the doc explicitly, I guess there's a reason I am missing anyway so we'll follow-up here for sure.

snicoll commented 7 years ago

See also #6769 (in particular https://github.com/spring-projects/spring-boot/issues/6769#issuecomment-278794709)

michael-simons commented 7 years ago

Hey, just a quick feedback from my colleague here, I tried to asked the questions about 41.3.2 in the docs as open as possible and he expressed the same expectations as I: @TestComponent would have the same visible result as @TestConfiguration (by visible result we mean have an impact in the application context, that is add to the configuration, appearing as bean (regardless if its component-scan or something else)). I think the dualism of @TestConfiguration is what causes or confusion with @TestComponent. Although @TestConfiguration is not picked by component scan, it has an impact.

michael-simons commented 7 years ago

Very good change in the docs! Thanks.

nightswimmings commented 5 years ago

I got the same confusion. If @TestComponent just filters out the component from the test context, I don't understand where it is used, so perhaps it would be better to focus on explaining when do you need such a feature. If I understand correctly by reading the docs, one uses @TestComponent to annotate a class that might be excluded by default and selectively importable via its TestConfiguration parent @Import for particular testclasses you need it. But then, whats the point on making it a @XComponent at all? Does @Import only accept configuration @XComponent classes perhaps? Or it is maybe because that way we don't need to remove annotations that signal the purpose of the class like Stereotypes in order to exclude them from the component scanning?

nightswimmings commented 5 years ago

I mean imagine I have a custom class AuxiliarHelperForMyTests class, and I don't annotate it with either @Component or @TestComponent. It exactly behaves as the description of @TestComponent, so what is exactly the purpose of @TestComponent annotation, what does it give me as extra value?

wilkinsona commented 5 years ago

A noted in the commit that closed this issue, @TestComponent is really an implementation detail so you should not be using it directly. It exists to enable the functionality provided by @TestConfiguration which is described in the documentation.

If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

nightswimmings commented 5 years ago

Ahh that explains why any mention was removed from latest documentation. Anyway, I did a deep testing and I will expose the case in the way my mind would have understood it, in case you accept the suggerence for documentation or anyone else comes to this ticket as I did. Please remove it if you consider it inappropiate. And excuses for mistaking the workflow if that is the case in beforehand:

If you have classes in test classpath that you want to explictly mark as Stereotypes (@Component) or @Configuration, but you don't want them loaded accross all tests by the component scanning, the solution is marking them additionally as @TestComponent and @TestConfiguration. This is the benefit (1) I see of these 2 new annotations.

Then you can use @Import on any particular test class to import any of both (BTW, it was hard to find out that Import works on Components as if they were @Beans from a @Configuration class). The curious thing is that @Import does not require classes to be annotated with either @Component or @Configuration to load them, hence the special @TestX marker versions are not justified as a device to exclude from classpath but mark as importable.

Remember static inner classes are not part of component-scanning when found within test classes, so using previous annotations as static inner classes inside a test class: