Skip to content

@MockitoBean with custom @Qualifier is not injected into @Configuration class #34646

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
duoduobingbing opened this issue Mar 25, 2025 · 2 comments
Assignees
Labels
in: test Issues in the test module type: bug A general bug
Milestone

Comments

@duoduobingbing
Copy link

duoduobingbing commented Mar 25, 2025

Problem

When a @MockitoBean is annotated with a custom @Qualifier it cannot be injected into a @Configuration class when there is a normal bean with said qualifier.

Reproducer

Reproducer Test
@ExtendWith(SpringExtension.class)
public class QualifierAnnotationTest {

    @MockitoBean
    @MyQualifier
    private IExample myExample;

    @Autowired
    private MyExampleCaller caller;

    @Test
    public void test() {

    }

    public interface IExample {

        String value();

    }

    public static class MyExampleCaller  {

        IExample example;

        public MyExampleCaller(IExample example) {
            this.example = example;
        }

    }

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyQualifier {

    }

    @MyQualifier
    public static class QualifierExample implements IExample {

        @Override
        public String value() {
            return "QualifierExample";
        }

    }

    @Configuration(proxyBeanMethods = false)
    static class TestConfig {

        @Bean
        public QualifierExample toBeReplacedByMock() {
            return new QualifierExample();
        }

        @Bean
        MyExampleCaller controller(@MyQualifier IExample example) {
            return new MyExampleCaller(example);
        }

    }
}

The test fails with:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.example.qualifiermockitobeanbug.QualifierAnnotationTest$IExample' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.example.qualifiermockitobeanbug.QualifierAnnotationTest.MyQualifier()}

Workaround

Replacing @MockitoBean with @MockBean causes the test to succeed.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 25, 2025
@sbrannen sbrannen changed the title MockitoBean with Qualifier is not injected into Configuration @MockitoBean with custom @Qualifier is not injected into @Configuration class Mar 25, 2025
@sbrannen sbrannen added the in: test Issues in the test module label Mar 25, 2025
@sbrannen sbrannen self-assigned this Mar 25, 2025
@sbrannen sbrannen added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 25, 2025
@sbrannen sbrannen added this to the 6.2.6 milestone Mar 25, 2025
@sbrannen
Copy link
Member

Hi @duoduobingbing,

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

And thanks for the well-crafted reproducer.

This turns out to be an interesting case. We have support for @Qualifier and various tests which verify that support; however, we did not have a test which exercises the scenario you have presented.

The core of the issue is that your toBeReplacedByMock() method declares QualifierExample as the return type (which is in fact annotated with @MyQualifier. However, you explicitly instructed Spring to create a mock based on the IExample interface via the @MockitoBean myExample field in the test class. Thus, the IExample mock effectively replaces what would otherwise have been a QualifierExample in the ApplicationContext.

In other words, in your example there is no longer a bean of type IExample in the context which is also annotated with @MyQualifier. Hence, the exception you encountered.

To work around this with @MockitoBean, you have a few options.

  1. Either annotate the toBeReplacedByMock() method with @MyQualifier.
  2. Or change the type of the myExample field to QualifierExample and remove the @MyQualifier declaration on the field.
  3. Or change the return type of the toBeReplacedByMock() method to IExample and annotate the method with @MyQualifier.

The latter two options are preferable, since they do not result in different types of beans being present in the final state of the ApplicationContext; whereas, option 1 (as well as your unmodified example when using Spring Boot's @MockBean) results in no bean of type QualifierExample in the ApplicationContext (as mentioned above), which could lead to bugs depending on what other components expect to be available in the context.

Although there are workarounds, we do consider this a bug since it worked in Spring Boot. Furthermore, we aim to have consistent support for custom @Qualifier annotations with the Bean Override support in Spring Framework as well. Consequently, I have been working on a fix for this that I will soon commit.

Regards,

Sam

@sbrannen
Copy link
Member

This has been fixed in 374c3b4 for inclusion in the upcoming 6.2.6 release.

If you would like to try it out before then, feel free to test against 6.2.6 snapshots.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants