Skip to content
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

Value properties with Duration type cannot be converted in unit test #34661

Closed
andreas-ajaib opened this issue Mar 21, 2023 · 4 comments
Closed
Labels
for: stackoverflow A question that's better suited to stackoverflow.com status: invalid An issue that we don't feel is valid

Comments

@andreas-ajaib
Copy link

andreas-ajaib commented Mar 21, 2023

Spring Boot version: 2.5.14

We have value properties like this:

@Value("${app.timeout}")
private Duration timeout;

It is working in normal application run, but it's not working in unit test:

nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'java.time.Duration'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.time.Duration': no matching editors or conversion strategy found

This is the structure of my unit test

@SpringJUnitConfig({Service.class, Config.class})
@TestPropertySource(properties = {"app.timeout=5s"})
class UnitTest {
   // unit tests here
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 21, 2023
@wilkinsona
Copy link
Member

Conversion of a String to a Duration is a Spring Boot feature supported by its ApplicationConversionService. You plain unit test doesn't appear to be using any Spring Boot features so this conversion isn't possible. If you use @SpringBootTest it should work. If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Please note that Spring Boot 2.5.x is no longer supported. You should upgrade to 2.7.x or later as soon as possible.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Mar 21, 2023
@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid for: stackoverflow A question that's better suited to stackoverflow.com and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 21, 2023
@andreas-ajaib
Copy link
Author

So do you say Spring won't be supporting this in plain unit tests? I see we already have @TestPropertySource that supports the property injection. So, I expect that Duration-typed property will also be working. Using @SpringBootTest is too much.

@philwebb
Copy link
Member

philwebb commented Jun 6, 2023

There is an open Spring Framework issue to add support, but currently you need to hook in Spring Boot's org.springframework.boot.convert.ApplicationConversionService.ApplicationConversionService.

If you don't want to do that using @SpringBootTest you might be able to use the initializers attribute of @SpringJUnitConfig and create an initializer that effectively does this.

@linarkou
Copy link

linarkou commented Feb 1, 2024

@philwebb thanks for your tip about initializers, it made the trick and I successfully ended up with this (tested on Spring Boot 2.7.10 & junit5)

@ExtendWith(SpringExtension.class)
@ContextConfiguration(initializers = DurationPropertyTest.ConversionInitializer.class)
@TestPropertySource(properties = {"app.timeout=10s"})
class DurationPropertyTest {
    static class ConversionInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.getBeanFactory().setConversionService(new ApplicationConversionService());
        }
    }

    @Value("${app.timeout}")
    private Duration timeout;

    @Test
    void testDurationConversion() {
        assertEquals(Duration.ofSeconds(10L), timeout);
    }
}

And one more way to solve the problem is just to load ConversionService bean into the test context

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = DurationPropertyTest.ConversionInitializer.class)
@TestPropertySource(properties = {"app.timeout=10s"})
class DurationPropertyTest {
    static class ConversionInitializer {
        @Bean
        public ConversionService conversionService() {
            return new ApplicationConversionService();
        }
    }

    @Value("${app.timeout}")
    private Duration timeout;

    @Test
    void testDurationConversion() {
        assertEquals(Duration.ofSeconds(10L), timeout);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: stackoverflow A question that's better suited to stackoverflow.com status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

5 participants