简体   繁体   中英

Mock @Value in SpringBoot unit test not working

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.

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");
  }

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM