13

I have a @Conditional bean -

@RestController("/user")
@ConditionalOnProperty(prefix = "user-controller", name = "enabled", havingValue = "true")
public void UserController {

@GetMapping
public String greetings() {
  return "Hello User";
}

}

it can be either enabled or disabled. I want to create a test to cover both use cases. How can I do that? I just have one application.properties file:

user-controller.enabled=true

I can inject the property into bean and add a setter to manage it via code, but that solution is not elegant:

@RestController("/user")
@ConditionalOnProperty(prefix = "user-controller", name = "enabled", havingValue = "true")
public void UserController {

@Value("${user-controller.enabled}")
private boolean enabled;

public void setEnabled(boolean enabled) {
 this.enabled = enabled;
}

@GetMapping
public String greetings() {
  return enabled ? "Hello User" : "Endpoint is disabled";
}

}

smth like this

idmitriev
  • 4,619
  • 4
  • 28
  • 44

2 Answers2

18

Assuming that you use SpringBoot 2 you might test like this:

public class UserControllerTest {

  private final ApplicationContextRunner runner = new ApplicationContextRunner()
      .withConfiguration(UserConfigurations.of(UserController.class));

  @Test
  public void testShouldBeDisabled() {
    runner.withPropertyValues("user-controller.enabled=false")
        .run(context -> assertThat(context).doesNotHaveBean("userController "));
  }
}
  • 1
    the proper syntax is: ```runner.withPropertyValues("user-controller.enabled=false") .run(context -> context.assertThat().doesNotHaveBean("userController"));``` – Skillz Mar 22 '22 at 17:09
  • Now (not sure since which version) `context.assertThat()` is deprecated and AssertJ's assertThat is preferred – Gijs Mar 21 '23 at 13:42
17

It is not a perfect solution (as it will load two Spring Boot application contexts and this takes time) but you could create two test classes, each one testing a particular case by setting the properties of @TestPropertySource or @SpringBootTest

@TestPropertySource(properties="user-controller.enabled=true")
public class UserControllerEnabledTest{...}

or

@SpringBootTest(properties="user-controller.enabled=true")
public class UserControllerEnabledTest{...}

in the test class that tests the enabled case and

@TestPropertySource(properties="user-controller.enabled=false")
public class UserControllerDisabledTest{...}

or

@SpringBootTest(properties="user-controller.enabled=false")
public class UserControllerDisabledTest{...}

in the test class that tests the disabled case.


A better solution would be probably to have a single class test.

if you use Spring Boot 1, you could check EnvironmentTestUtils.addEnvironment.

if you use Spring Boot 2, you could check TestPropertyValues.

Denis Abakumov
  • 355
  • 3
  • 11
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • thx, @SpringBootTest(properties="user-controller.enabled=false") works fine, just one disadvantage - two contexts will be loaded, but I've not found better solution – idmitriev Dec 13 '17 at 11:58
  • I agree with you. I am not fan of this solution either. Tests should be as short as possible in terms of time processing. But hard to make better with an integration test as the bean to test is loaded during container loading. – davidxxx Dec 13 '17 at 13:01
  • yeap, ideally it should be implemented like bean refreshing in Spring Cloud Config. When I can mark a bean as @RefreshScope and then invoke refresh and just that bean will be reloaded with new property value. – idmitriev Dec 13 '17 at 14:31
  • but I don't wanna do any other movements to achive that. Your proposed solution works for me. – idmitriev Dec 13 '17 at 14:33