Skip to content

Unable to mock a Spring primary bean when other instances are available #1502

@konradczajka

Description

@konradczajka

Describe the bug

In larger Spring Boot's codebases it's not uncommon to have multiple beans of the same type where one of them is marked as @Primary.

Attempting to mock such bean with @SpringBean doesn't work as Spock Spring requires exactly one mocked bean's instance in the context (regardless of whether one of them is marked as primary).

This is an unnecessary limitation, especially that spring-boot-starter-test doesn't pose such limit when using @MockBean.

You can find a sample project presenting this issue with tests written using both SpringBean and MockBean here:
https://github.com/konradczajka/spock-spring-issue

The problem seems to be located in SpockMockPostprocessor which doesn't checks if one of located beans is marked as primary. I'd gladly provide a PR with a solution which seems to be working locally (which is highly inspired by the spring-boot-starter-test's code)

To Reproduce

With context configured as follows:

@Bean
Service2 service2NotPrimary() {
    new Service2()
}

@Primary
@Bean
Service2 service2Primary() {
    new Service2()
}

@Bean
IService1 service1(Service2 service2) {
    return new Service1(service2)
}

and a test like this:

@SpringBean
Service2 service2 = Mock() {
    generateQuickBrownFox() >> "blubb"
}

@Autowired
Service1 service1

def "injection with stubbing works"() {
    expect:
    service1.generateString() == "blubb"
}

Expected behavior

Passing test

Actual behavior

The test fails with a message similar to this:

Unable to register mock beanService2 expected a single matching bean to replace but found [service2Primary, service2NotPrimary]

Java version

18, but different versions don't affect the outcome

Buildtool version

18, but different versions don't affect the outcome

What operating system are you using

Linux

Dependencies

plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'java'
	id 'groovy'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.spockframework:spock-core:2.1-groovy-3.0'
	testImplementation 'org.spockframework:spock-spring:2.1-groovy-3.0'
}

Additional context

The same test written in Java/Mockito with spring-boot-starter-test and its @MockBean works as intended.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions