TanjaMUC
TanjaMUC

Reputation: 61

Test for Spring Boot @ConfigurationProperties validation fails

I want to write a test for a @NotNull, @NotEmpty validation of @ConfigurationProperties.

@Configuration
@ConfigurationProperties(prefix = "myPrefix", ignoreUnknownFields = true)
@Getter
@Setter
@Validated
public class MyServerConfiguration {
  @NotNull
  @NotEmpty
  private String baseUrl;
}

My Test looks like this:

@RunWith(SpringRunner.class)
@SpringBootTest()
public class NoActiveProfileTest {
  @Test(expected = org.springframework.boot.context.properties.bind.validation.BindValidationException.class)
  public void should_ThrowException_IfMandatoryPropertyIsMissing() throws Exception {
  }

}

When I run the test, it reports a failure to start the application before the test is run:

***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target   org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myPrefix' to com.xxxxx.configuration.MyServerConfiguration$$EnhancerBySpringCGLIB$$4b91954c failed:

How can I expect an Exception to write a negative test? Even if I replace the BindException.class with Throwable.class the application fails to start.

Upvotes: 5

Views: 2034

Answers (2)

Miloš Milivojević
Miloš Milivojević

Reputation: 5369

I'd use an ApplicationContextRunner for this, e.g.

new ApplicationContextRunner()
    .withUserConfiguration(MyServerConfiguration.class)
    .withPropertyValues("foo=bar")
    .run(context -> {
        var error = assertThrows(IllegalStateException.class, () -> context.getBean(MyServerConfiguration.class));

        var validationError = (BindValidationException) ExceptionUtils.getRootCause(error);
        var fieldViolation = (FieldError) validationError.getValidationErrors().iterator().next();
        var fieldInError = fieldViolation.getObjectName() + "." + fieldViolation.getField();

        assertThat(fieldInError, is(expectedFieldInError));
        assertThat(fieldViolation.getDefaultMessage(), is(expectedMessage));
    });

Upvotes: 3

qwazer
qwazer

Reputation: 7521

Try to load Spring Boot Application context programmatically:

Simple version

public class AppFailIT {

    @Test
    public void testFail() {
        try {
            new AnnotationConfigServletWebServerApplicationContext(MyApplication.class);
        }
        catch (Exception e){
            Assertions.assertThat(e).isInstanceOf(UnsatisfiedDependencyException.class);
            Assertions.assertThat(e.getMessage()).contains("nested exception is org.springframework.boot.context.properties.bind.BindException: Failed to bind properties");
            return;
        }
        fail();
    }
}

Extended version with ability to load environment from application-test.properties and add own key:values to test environment on method level:

@TestPropertySource("classpath:application-test.properties")
public class AppFailIT {

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired
    private ConfigurableEnvironment configurableEnvironment;

    @Test
    public void testFail() {
        try {
            MockEnvironment mockEnvironment = new MockEnvironment();
            mockEnvironment.withProperty("a","b");
            configurableEnvironment.merge(mockEnvironment);
            AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext();
            applicationContext.setEnvironment(configurableEnvironment);
            applicationContext.register(MyApplication.class);
            applicationContext.refresh();
        }
        catch (Exception e){
            Assertions.assertThat(e.getMessage()).contains("nested exception is org.springframework.boot.context.properties.bind.BindException: Failed to bind properties");
            return;
        }
        fail();
    }
}

Upvotes: -1

Related Questions