'@TestPropertySource is not helping to fetch configuration property values

In the following test code snippet, values of number_of_days.last and number_of_months.plan are not getting fetched from the configuration file.Please check and see what could be the reason.When i remove @Value annotation from my service class ShiftPlanService.java and just initialize the values there with the required values,the test passes.

@ExtendWith(MockitoExtension.class)
@ContextConfiguration(classes=SpringbootMysqlExampleApplication.class)
@TestPropertySource(locations="src/main/resources/application.properties",properties= {"number_of_days.last= 7","number_of_months.plan= 2"})
class ShiftPlanServiceTest {
        @Mock
        ShiftPlanRepo mockedSpr;
    
        @Mock(lenient = true)
        ShiftDetailsRepo mockedSdr;
    
        @Mock(lenient = true)
        EmployeeDetailsRepo mockedEdr;
    
        @Spy
        ShiftPlanService sps;
    
        @BeforeEach
        public void setUp() {
            when(mockedSdr.findShiftNameById(1)).thenReturn("Morning");
            when(mockedSdr.findShiftNameById(2)).thenReturn("Afternoon");
            when(mockedEdr.getNameById(0)).thenReturn("Amit");
            when(mockedEdr.getNameById(1)).thenReturn("Anupam");
            when(mockedEdr.getNameById(2)).thenReturn("Chirag");
            when(mockedEdr.getNameById(3)).thenReturn("Rashmi");
            when(mockedEdr.count()).thenReturn(4L);
        }
    
        @Test
        public void testCreateShiftPlan() {
            sps.createShiftPlan(4, 1, 2020);
            verify(mockedSpr, times(36)).save(any(ShiftPlan.class));
            verifyNoMoreInteractions(mockedSpr);
        }
   } 

application.properties file is as follows-

server.port=8104
number_of_days.last= 7
number_of_months.plan= 2
spring.datasource.url=<<sensitive info>>
spring.datasource.username=<sensitive info>
spring.datasource.password=<sensitive info>

#Keep the connection alive while idle for a long time
spring.datasource.testWhileIdle= true
spring.datasource.validationQuery= SELECT 1

# Show or not log for each sql query
spring.jpa.show-sql = true

# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update

# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

# Use spring.jpa.properties.* for Hibernate native properties (the prefix is
# stripped before adding them to the entity manager)

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect  

In the ShiftPlanService class, i have

@Value("${number_of_days.last}")
public int ndl;

@Value("${number_of_months.plan}")
public int nm;


Solution 1:[1]

I think you are intending to use a real instance of ShiftPlanService and have mocks injected. You need to have Spring autowire the ShiftPlanService into your test and tell it to inject the mocks like this:

@Autowired
@InjectMocks
ShiftPlanService sps;

Though you may consider just instantiating ShiftPlanService yourself in your setup method and just pass your mocks and set the other properties on the ShiftPlanService instead.

Solution 2:[2]

You are confusing Mockito injection with Spring injection. @Value is a Spring concept and will only be injected when Spring manages the bean, but the instance of ShiftPlanService you have in your test is injected by Mockito using @Spy (which as has been pointed out you don't really need).

My recommendation would be to decide what you want - a unit test with mocks, or a full-blown Spring test with the application context running. It seems to me that your intent is to write a unit test with everything mocked out, in which case:

  • remove @ContextConfiguration and @TestPropertySource (you don't need those for unit tests)
  • use @InjectMocks instead of @Spy on ShiftPlanService sps - it will most likely do what you want, depending on how ShiftPlanService is implemented
  • manually set the config values you need in sps; you can add setters for those for the test to use; if the unit test is in the same package as the class - which is a good practice - they can be package-private too - because in production Spring will autowire them for you, so you only need them for the test
  • oh, and keep the @Value annotation in your ShiftPlanService - it's needed for production - as explained above

Solution 3:[3]

We invented a Mockito extension that allows for easy injection of String/Integer/Boolean properties. Checkout the readme, it's super easy to use and works along with @InjectMocks

https://github.com/exabrial/mockito-object-injection

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Jonathan S. Fisher