Home > Software engineering >  Mock @Value in SpringBoot unit test not working
Mock @Value in SpringBoot unit test not working

Time:04-26

I am trying to get some junit tests with Mockito to work in a SprinBoot application.

Now my Service has some variable that gets filled from the application.properties by means of @Value annotation:

@Component
@Slf4j
public class FeatureFlagService {

  @Autowired
  RestTemplate restTemplate;

  @Value("${url.feature_flags}")
  String URL_FEATURE_FLAGS;

// do stuff
}

I am trying to test this by using TestPropertySource like so:

@ExtendWith(MockitoExtension.class)
@TestPropertySource(properties = { "${url.feature_flags} = http://endpoint" })
class FeatureFlagServiceTests {

  @Mock
  RestTemplate restTemplate;

  @InjectMocks
  FeatureFlagService featureFlasgService;

  @Test
  void propertyTest(){
    Assertions.assertEquals(featureFlasgService.URL_FEATURE_FLAGS, "http://endpoint");
  }

However the property does not get filled and remains null.

There are a bunch of tpoics on this, but I have not been able to piece together a solution. I saw solutions suggesting @SpringBootTest, but then it seems to want to do an integration test, spinning up the service, which fails because it can not connect to DB. So that is not what I am looking for.

I also saw solutions suggesting I make a PropertySourcesPlaceholderConfigurer bean. I tried that by putting :

  @Bean
    public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
    return new PropertySourcesPlaceholderConfigurer();
  }

In my Application.java. But that is not working / not enough. I am not sure if I was supposed to do that differently, or if there is more there that I do not know.

Please advice.

CodePudding user response:

You can use @SpringBootTest without running the whole application by passing it the class that contains the @Value but you have to use Spring's extension @ExtendWith({SpringExtension.class}) which is included inside @SpringBootTest and by that using Spring's MockBean instead of @Mock and @Autowired for autowiring the bean like this:

@SpringBootTest(classes = FeatureFlagService.class)
class FeatureFlagServiceTests {

  @MockBean
  RestTemplate restTemplate;

  @Autowired
  FeatureFlagService featureFlasgService;

  @Test
  void propertyTest(){
    Assertions.assertEquals(featureFlasgService.URL_FEATURE_FLAGS, "http://endpoint");
  }

CodePudding user response:

I recommend you to try this approach. Just need a slightly refactoring and add a package-private constructor to your FeatureFlagService.

FeatureFlagService.java

@Component
@Slf4j
public class FeatureFlagService {

    private final RestTemplate restTemplate;
    private final String URL_FEATURE_FLAGS;

    // package-private constructor. test-only
    FeatureFlagService(RestTemplate restTemplate, @Value("${url.feature_flags}") String flag) {
        this.restTemplate = restTemplate;
        this.URL_FEATURE_FLAGS = flag;
    }

    // do stuff
}

Then prepare your mocks and url, and inject them by the constructor-injection.

FeatureFlagServiceTests.java

public class FeatureFlagServiceTests {

    private FeatureFlagService featureFlagService;

    @Before
    public void setup() {
        RestTemplate restTemplate = mock(RestTemplate.class);
        // when(restTemplate)...then...
        String URL_FEATURE_FLAGS = "http://endpoint";
        featureFlagService = new FeatureFlagService(restTemplate, URL_FEATURE_FLAGS);
    }

    @Test
    public void propertyTest(){
        Assertions.assertEquals(featureFlasgService.getUrlFeatureFlags(), 
        "http://endpoint");
    }
}

The significant advantage is, your FeatureFlagServiceTests becomes very simple to read and trivial to test. You don't need magic annotation of Mockito anymore.

  • Related