How to mock class with @ConfigurationProperties in Spring Boot
Asked Answered
P

3

5

I have a class that Autowires another class with @ConfigurationProperties.

Class with @ConfigurationProperties

@ConfigurationProperties(prefix = "report")
public class SomeProperties {
    private String property1;
    private String property2;
...

Class that Autowires above class SomeProperties

@Service
@Transactional
public class SomeService {
    ....
    @Autowired
    private SomeProperties someProperties;
    .... // There are other things

Now, I want to test SomeService class and in my test class when I mock SomeProperties class, I am getting null value for all the properties.

Test class

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeProperties.class)
@ActiveProfiles("test")
@EnableConfigurationProperties
public class SomeServiceTest {
    @InjectMocks
    private SomeService someService;

    @Mock // I tried @MockBean as well, it did not work
    private SomeProperties someProperties;

How can I mock SomeProperties having properties from application-test.properties file.

Panacea answered 11/9, 2020 at 14:39 Comment(1)
Use constructor injection, not field injection.Ruble
E
3

You are not mocking SomeProperties if you intend to bind values from a properties file, in which case an actual instance of SomeProperties would be provided.

Mock:

@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {

    @InjectMocks
    private SomeService someService;

    @Mock
    private SomeProperties someProperties;

    @Test
    public void foo() {
        // you need to provide a return behavior whenever someProperties methods/props are invoked in someService
        when(someProperties.getProperty1()).thenReturn(...)
    }

No Mock (someProperties is an real object that binds its values from some propertysource):

@RunWith(SpringRunner.class)
@EnableConfigurationProperties(SomeConfig.class)
@TestPropertySource("classpath:application-test.properties")
public class SomeServiceTest {
   
    private SomeService someService;

    @Autowired
    private SomeProperties someProperties;

    @Before
    public void setup() {
        someService = new someService(someProperties); // Constructor Injection
    }
    ...
Edging answered 11/9, 2020 at 20:27 Comment(5)
Do we need constructor injection for the autowired propertyAntimonous
@Antimonous What''s being @Autowired here doesn't have dependency on other Beans. So no. But in the case where dependency beans are present in the @Autowired bean, you could mock the dependency beans with @MockBean and provide stubbing for them.Catton
I tried this solution but it doesn't work, the only difference is that I am using the .yml file instead of the properties file. Maybe the spring boot version is old since the project is legacy :) Do you have any idea?Antimonous
@Antimonous the reason is @TestPropertySource does not support YAML property files. Instead of this annotation you have provide special initializer via @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class) or ConfigFileApplicationContextInitializer.class if you're using Spring Boot older than 2.4.Helminthiasis
@NikitaKobtsev thank you for the explanation. I changed my yaml property files with original ones. but now, i know why i couldn't use @ TestPropertySource :)Antimonous
F
3

You need to stub all the property values in @Test/@Before methods of SomeServiceTest.java like :

ReflectionTestUtils.setField(someProperties, "property1", "value1");

ReflectionTestUtils.setField(object, name, value);

                         

You can also mock the reponse for dependent classes via Mockito.when()

Fluke answered 11/9, 2020 at 17:11 Comment(0)
E
3

You are not mocking SomeProperties if you intend to bind values from a properties file, in which case an actual instance of SomeProperties would be provided.

Mock:

@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {

    @InjectMocks
    private SomeService someService;

    @Mock
    private SomeProperties someProperties;

    @Test
    public void foo() {
        // you need to provide a return behavior whenever someProperties methods/props are invoked in someService
        when(someProperties.getProperty1()).thenReturn(...)
    }

No Mock (someProperties is an real object that binds its values from some propertysource):

@RunWith(SpringRunner.class)
@EnableConfigurationProperties(SomeConfig.class)
@TestPropertySource("classpath:application-test.properties")
public class SomeServiceTest {
   
    private SomeService someService;

    @Autowired
    private SomeProperties someProperties;

    @Before
    public void setup() {
        someService = new someService(someProperties); // Constructor Injection
    }
    ...
Edging answered 11/9, 2020 at 20:27 Comment(5)
Do we need constructor injection for the autowired propertyAntimonous
@Antimonous What''s being @Autowired here doesn't have dependency on other Beans. So no. But in the case where dependency beans are present in the @Autowired bean, you could mock the dependency beans with @MockBean and provide stubbing for them.Catton
I tried this solution but it doesn't work, the only difference is that I am using the .yml file instead of the properties file. Maybe the spring boot version is old since the project is legacy :) Do you have any idea?Antimonous
@Antimonous the reason is @TestPropertySource does not support YAML property files. Instead of this annotation you have provide special initializer via @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class) or ConfigFileApplicationContextInitializer.class if you're using Spring Boot older than 2.4.Helminthiasis
@NikitaKobtsev thank you for the explanation. I changed my yaml property files with original ones. but now, i know why i couldn't use @ TestPropertySource :)Antimonous
R
1

If you are using @Mock , you need to stub the values as well. By default the value will be null for all the properties.

Rosetta answered 11/9, 2020 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.