-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Support constructor injection without @Autowired when using JUnit Jupiter in spring-test #22286
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
Comments
Thanks for raising the issue! It has actually been requested through various channels, so I think we should indeed take some action here.
I don't think we would implement this feature like that, since that would result in early (and potentially unnecessary) creation of the I would rather make automatic autowiring of constructors an opt-in feature. The challenge is coming up with a good way to allow users to easily opt into it. Another option is to turn on this feature by default and provide a way to opt out of it, but that would potentially be a breaking change for some users -- for example, if a test class constructor previously declared an |
If you're using Lombok you can actually instruct Lombok to annotate the generated constructor as demonstrated in this blog. Thus, the following should work for your use case. @SpringJUnitConfig(ContextBean.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
class Junit5WithLombokTest {
private final DataSource dataSource;
@Test
void test() {
System.out.println("test-datasource: " + dataSource);
}
} Let me know if that works for you. |
Thanks for your reply. Yes, Another implementation is to support @Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
Parameter parameter = parameterContext.getParameter();
int index = parameterContext.getIndex();
Executable executable = parameter.getDeclaringExecutable();
return ParameterAutowireUtils.isAutowirable(parameter, index) ||
executable instanceof Constructor;
} It works for me too. It checks the annotations always and fallback to check a constructor. I think this implementation is better than that I proposed earlier. You can also consider a better way to implement this feature. |
…it Jupiter in spring-test See gh-22286
For anyone interested, the current work on this feature can be seen in the following feature branch. https://github.com/spring-projects/spring-framework/compare/issues/gh-22286 |
Thanks, Sam. This looks great already! I'm wondering if we could find a better name for the annotation enabling this feature. I know that the latter imply that the class annotated is actually becoming a Spring bean eventually which the test case is not. But that feels like an implementation detail leaking through. I still think there's beauty in the symmetry of:
On a quick thought If the annotation is supposed to be used in combination with |
That's because the test class is annotated with |
Interesting points regarding the naming. You are correct that it is a "verb" rather than a noun or adjective like most annotations in What do you think about that? I'm not very keen on naming this annotation anything related to components, since the use of this annotation doesn't actually imply that the test class is dedicated to testing components per se. I am also not very keen on naming it anything like In general, this annotation is only being introduced in order to allow developers to override the global default flag for "automatic autowiring of test constructors". As such, I envision that this annotation will typically only be used in one-off scenarios or as a meta-annotation (perhaps in Spring Boot Test) to enforce a certain opinionated style of testing. In other words... I imagine that people like you and @sdeleuze will typically set Does that make more sense, now that I've given the rationale for the annotation and the global flag? |
tl;drI'd love to be able to get a Spring integration test case running with a single annotation that causes the test class to be handled as much as possible like a Spring component in the first place to keep the conceptual differences between test classes and production components as little as possible: @SpringJUnitConfig
@AutowireTestConstructor
@TestInstance(Lifecycle.PER_CLASS)
// other annotations needed
@interface SpringTest {
// add aliases for attributes of above annotations
}
@SpringTest
@TestPropertySource(…)
class MyIntegrationTest {
// Autowire constructor without explicit annotation
// Singleton lifecycle by default
} DetailsI understand there are knobs to tweak the defaults and make this work globally. I still think it's suboptimal to leak so much implementation detail into the naming. Ultimately it is about the out of the box simplicity and consistency in the programming model. Let's take a step back and forget everything we know about how Spring works internally. Now let me introduce Spring's component model to you:
If I now move on to tests as classes with a special role in the code base, anything that deviates from that model ("Annotate the class to assign it a role, which then allows you to use role specific features on methods") adds cognitive load and feels like a seam between classes with the special test role and production code. There's no reason there should be a conceptual difference, except the stereotype. I might have to deal with specialties and learn about additional annotations, but by default there should be a way to get these tests running as close as possible to the way Spring handles components in terms of features and lifecycle. The reason I think this is so important is that I think Spring Framework's strength is in the simplicity of the component model as it incentivizes good class design in the first place: thread-safe, immutable types, dependencies handed into classes via constructor parameters, container managed singleton instances by default. With JUnit 5 this is possible now as well. Making the test class work the exact same way would reduce the need to constantly context switch between "How does this work for an application component?" and "How does this work for a test class?". Effectively, they're types with slightly different roles in your application. If JUnit has other defaults, that's fine. But with the new generation we have the ability to let Spring related integration tests work as close as possible to the Spring component model. In that light, would it make sense to configure
The reason I was puzzled is this line in the Javadoc:
That makes me think, I need to use the annotation. I look at the test and don't see it anywhere. Using meta-annotations can sometimes make it a bit hard to follow where stuff comes from and more importantly what do I need to get things running. Would it be an option to annotate the annotation to be introduced with Alternatively, if Wondering what @jhoeller thinks about this. |
@odrotbohm, thanks for the detailed write up! I now see exactly where you're going with this line of thought, and I would appreciate it if you would open a separate issue to address the introduction of a new composed annotation to meet your goals. Rationale: this GitHub issue is dedicated to the mechanics of how to support automatic autowiring of test constructors via a global flag and an annotation for overriding the global flag, and we need this as a building block for your proposal in any case. As such, the two topics are orthogonal, and I'll go ahead and complete the tasks for this issue as-is. Cheers, Sam |
Prior to this commit, dependency injection of all arguments in a test class constructor invoked by JUnit Jupiter was only possible if the constructor was explicitly annotated with @Autowired. This commit introduces support for a configurable "test constructor autowire" mode which defaults to false in order to remain backwards compatible. Specifically, this mode can be configured globally for an entire test suite via a new "spring.test.constructor.autowire" JVM system property that can alternatively be configured via the SpringProperties mechanism. In addition, the global "test constructor autowire" mode can be overridden locally on a per-class basis via the new @Testconstructor annotation. Closes gh-22286
Reopening to add corresponding documentation to the Reference Manual. |
To be addressed in gh-22928. |
Affects: 5.0 GA
In the current implementation,
org.springframework.test.context.junit.jupiter.ParameterAutowireUtils.isAutowirable(Parameter parameter, int parameterIndex)
checks elements annotated with@Autowired
,@Qualifier
and@Value
only.When the JUnit Jupiter test class like below, it will not work properly.
The above code will produce an error below:
Why not check the parameter name or type whether exists in current ApplicationContext?
For example:
The text was updated successfully, but these errors were encountered: