Overview

Spring 3.0 added support for JSR-330. However, the spring-test module is only supporting @Autowired for dependency injection.

This PR intends to add @Inject support for spring-test. Since Spring Framework 6 still supports @javax.inject.Inject in addition to @jakarta.inject.Inject, this PR reintroduces support for @javax.inject.Inject in spring-test as well.

Examples

The following test passes:

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;

@SpringJUnitConfig
public class FooTests {

    private final String foo;

    @Autowired
    public FooTests(String foo) {
        this.foo = foo;
    }

    @Test
    public void beanInjected() {
        assertThat(this.foo).isEqualTo("foo");
    }

    @Configuration
    static class Config {

        @Bean
        String foo() {
            return "foo";
        }

    }
}

However, if @Autowired is replaced with @javax.inject.Inject the test fails:

package com.example.demo;

import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;

@SpringJUnitConfig
public class FooTests {

    private final String foo;

    @Inject
    public FooTests(String foo) {
        this.foo = foo;
    }

    @Test
    public void beanInjected() {
        assertThat(this.foo).isEqualTo("foo");
    }

    @Configuration
    static class Config {

        @Bean
        String foo() {
            return "foo";
        }

    }
}

Error

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String foo] in constructor [public com.example.demo.FooTests(java.lang.String)].

Comment From: sbrannen

I've edited your comment to improve the formatting. You might want to check out this Mastering Markdown guide for future reference.

Comment From: sbrannen

Hi @FlorianLehmann,

Congratulations on submitting your first PR for the Spring Framework! 👍

However, the spring-test module is only supporting @Autowired for dependency injection.

That's not entirely true.

The Spring TestContext Framework supports whatever DI annotations are supported by AutowiredAnnotationBeanPostProcessor, which includes @Autowired and @Value as well as @jakarta.inject.Inject and @javax.inject.Inject (if the latter two are available on the classpath).

Thus, you are free to use @Inject for fields and methods in your test classes.

The only restriction regarding @Autowired vs @Inject is that @Inject cannot be used for autowiring test class constructors when using JUnit Jupiter (but @Autowired can).

In addition, @Inject cannot be used to autowire a particular constructor or method argument simply because @Inject is configured with @Target({ METHOD, CONSTRUCTOR, FIELD }). So that limitation is inherent to @Inject and therefore out of Spring's control.

If I recall correctly, the latter is the reason that we decided not to support @Inject for JUnit Jupiter features: we wanted to support @Autowired which can be used consistently for test class constructors as well as individual constructor/method arguments.

However, we will consider adding support for @Inject for test class constructors, and I have changed the title of this issue to reflect that.

Comment From: FlorianLehmann

Hi @sbrannen,

I've edited your comment to improve the formatting. You might want to check out this Mastering Markdown guide for future reference.

Thank you I'll definitely have a look.

In addition, @Inject cannot be used to autowire a particular constructor or method argument simply because @Inject is configured with @Target({ METHOD, CONSTRUCTOR, FIELD }). So that limitation is inherent to @Inject and therefore out of Spring's control.

I believe the same limitation applies for "normal" constructor or method arguments. The idea of this PR is to remove the discrepancy between tests and "normal" code when autowiring a constructor using @Inject.

However, we will consider adding support for @Inject for test class constructors, and I have changed the title of this issue to reflect that.

:+1:

Thank you again for your comments.

Comment From: sbrannen

This has been merged into main in 8dd857a84d7f5d8c7903f445303906c887c70281 and revised in dfea3d05aa4abd0493a2714c0a77edd13a4a2f18, for inclusion in Spring Framework 6.1.

Thanks for your contribution, @FlorianLehmann! 🍃