Tomas
Tomas

Reputation: 530

Mocked named beans in Spring configuration without using allow-bean-definition-overriding?

I have two beans with the same signature. They are named in order to get the correct instance to the classes requesting them.

@Configuration
public class MyConfiguration {
  @Bean("durationForX")
  public Duration durationForX() {
    return Duration.ofSeconds(1);
  }

  @Bean("durationForY")
  public Duration durationForY() {
    return Duration.ofSeconds(5);
  }
}

and used as

@Component
public class MyService {
  public MyService(
     @Qualifier("durationForX") duration
  ) {
     ...
  }
}

and similar for Y.

Now, I want to have mocks of the above beans autowired in an integration test. I've tried the following

@Configuration
@Profile("my-test-profile")
public class IntegrationTestConfiguration {
  @Primary
  @Bean("durationForX")
  public Duration durationForXMock() {
    return Duration.ofMillis(100);
  }

  @Primary
  @Bean("durationForY")
  public Duration durationForYMock() {
    return Duration.ofMillis(500);
  }

  @Primary
  @Bean
  public AnotherService anotherService() {
     // This one works as expected, probably because it is not a named bean
     ...
  }
}

which, when running the integration tests, results in the error message

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'durationForX', defined in class path resource [com/../../MyConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/.../.../IntegrationTestConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

I'm not auto-wiring the instances themselves in the integration tests, only one entry point for the application in order to call it.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {MyApp.class})
@ActiveProfiles("it")
class MyIntegrationTest {
    @Autowired
    GraphQLTestTemplate graphQL;

   ...
}

I'm not too keen on setting the bean override to true as I want to be in control of which beans are used where. I would expect mocking the named beans to follow the same pattern as the not named one, why is this? Any idea on workarounds?

Upvotes: 1

Views: 320

Answers (1)

Ryuzaki L
Ryuzaki L

Reputation: 40078

I would recommend using the different profile for test, for example you can define the values in main application.yml for main application

application.yml

duration1:1
duration2:5

And then in read them in MyConfiguration class using @Value annotation

@Configuration
public class MyConfiguration {


    @Value("${duration1})
    private Integer duration1;

    @Value("${duration2})
    private Integer duration2;

    @Bean("durationForX")
    public Duration durationForX() {
         return Duration.ofSeconds(duration1);
    }

    @Bean("durationForY")
    public Duration durationForY() {
       return Duration.ofSeconds(duration2);
   }

}

Now for test create application-test.yml under src/main/resources or src/test/resources, then add the properties with test values

application-test.yml

duration1:100
duration2:500

No need of any IntegrationTestConfiguration file's you can just maintain test properties in test.yml file

Note : Make sure you annotate test class with @Profile("test") and @SpringBootTest to load the test ap[plication context with corresponding test properties

@SpringBootTest
@Profile("test)
public class AppTest {

  }

Upvotes: 1

Related Questions