0

I have a configuration class that uses a properties file and it works properly. Now I want to test that code and I have to recognize that the method annotated with @PostConstruct is run twice during the test. (In debug mode I can see that the for-loop is conducted twice.)

The configuration class:

@Slf4j
@RequiredArgsConstructor
@Configuration
@ConfigurationPropertiesScan("com.foo.bar")
public class MyConfig {

    private final MyProperties myProperties;

    @Autowired
    private GenericApplicationContext applicationContext;

    @PostConstruct
    void init() {
        Objects.requireNonNull(myProperties, "myProperties may not be null");
        for (final MyProperties.MyNestedProperty nested : myProperties.getApps()) {
            log.info("xxx {} created.", nested.getName());
            applicationContext.registerBean(nested.getName(), MyContributor.class, nested);
        }
    }
}

The used properties class:

@Slf4j
@Data
@Validated
@ConfigurationProperties(prefix = MyProperties.CONFIG_PREFIX)
public class MyProperties {

    public static final String CONFIG_PREFIX = "xxx";

    @Valid
    @NestedConfigurationProperty
    private List<MyNestedProperty> apps;

    @Data
    public static class MyNestedProperty {
        @NotNull
        @NotEmpty
        private String abc;

        private String xyzzy;

        @NotNull
        @NotEmpty
        private String name;
    }
}

My attempt with the test class:

@ExtendWith(SpringExtension.class)
@RequiredArgsConstructor
@ContextConfiguration(classes = MyConfigTest.MyTestConfiguration.class)
class MyConfigTest {
    @MockBean
    MyProperties myProperties;

    ApplicationContextRunner context;

    @BeforeEach
    void init() {
        context = new ApplicationContextRunner()
            .withBean(MyProperties.class)
            .withUserConfiguration(MyConfig.class)
        ;
    }

    @Test
    void should_check_presence_of_myConfig() {

        context.run(it -> {
            assertThat(it).hasSingleBean(MyConfig.class);
        });
    }

//    @Configuration
    @SpringBootConfiguration
//    @TestConfiguration
    static class MyTestConfiguration {
        @Bean
        MyProperties myProperties() {
            MyProperties myProperties = new MyProperties();
            MyProperties.MyNestedProperty nested = new MyProperties.MyNestedProperty();
            nested.setName("xxx");
            nested.setAbc("abc");
            nested.setXyz("xyz");
            myProperties.setApps(List.of(nested));
            return myProperties;
        }
    }
}

Why does this happen and how can I prevent this behaviour?

João Dias
  • 16,277
  • 6
  • 33
  • 45
du-it
  • 2,561
  • 8
  • 42
  • 80
  • How many tests do you have in `MyConfigTest`? – João Dias Jan 26 '22 at 22:18
  • 3
    You are running it twice yourself... First it is started/loaded with the `@ExtendWith`, secondly you are creating a new context again in your `init` method. Hence the class is instantiated twice by Spring, however this is only due to your own code. Looking at your test the `@ExtendWith` and `@ContextConfiguration` are pretty much useless due to the way you test. – M. Deinum Jan 27 '22 at 07:54
  • I just only have should_check_presence_of_myConfig() – du-it Jan 28 '22 at 08:41
  • Additional to your suggestion to remove the `@ExtendWith` the problem was that I didn't use `.withUserConfiguration(MyTestConfiguration.class)` to add it to the context. `@Configuration` is sufficient. I don't have to use `@SpringBootContext`. – du-it Jan 28 '22 at 09:08

0 Answers0