Search code examples
javaspringspring-bootunit-testingjunit

Mocking a configuration property when testing a Spring bean


I have a Spring bean that reads a configuration property value from application.yml

public class AutoDisableRolesService {

    @Value("${cron.enabled}")
    private boolean runTask;
    // remainder of class omitted
}

In the application.yml this property is set to false

cron:
  enabled: false

But when I run the test, I want it to be true. I've tried the following, but it does not seem to work

@SpringBootTest(properties = { "cron.enabled=true" })
@ExtendWith(MockitoExtension.class)
public class AutoDisableRolesServiceTests {
    
    @Mock
    private UserRoleRepository userRoleRepository;

    @InjectMocks
    private AutoDisableRolesService autoDisableRolesService;
    // remainder of test class omitted
}

I've also tried the following, without success

@ContextConfiguration(classes = AutoDisableRolesService.class)
@TestPropertySource(properties = "cron.enabled=true")
@ExtendWith(MockitoExtension.class)
public class AutoDisableRolesServiceTests {

    @Mock
    private UserRoleRepository userRoleRepository;

    @InjectMocks
    private AutoDisableRolesService autoDisableRolesService;
    // remainder of test class omitted
}

Solution

  • You're mixing up two types of test set ups; A Spring boot test setup with a Mockito test set up. By using @InjectMocks on your class under test, Mockito instantiates the class and injects all the fields annotated with @Mock, bypassing the Spring TestApplicationContext setup.

    Either use a Spring test set up using:

    @SpringBootTest(properties = { "cron.enabled=true" })
    public class AutoDisableRolesServiceTests {
        
        @MockBean
        private UserRoleRepository userRoleRepository;
    
        @Autowired
        private AutoDisableRolesService autoDisableRolesService;
    
        // remainder of test class omitted
    }
    
    

    Or a mockito set up using:

    public class AutoDisableRolesServiceTests {
        
        @Mock
        private UserRoleRepository userRoleRepository;
    
        @InjectMocks
        private AutoDisableRolesService autoDisableRolesService;
    
        @BeforeEach
        public void setUp() {
            ReflectionTestUtils.setField(autoDisableRolesService, "runTask", true);
        }
    }
    
    

    [edit]

    If you don't need a full @SpringBootTest set up, use

    @ExtendWith(SpringExtension.class)
    @TestPropertySource(properties={"cron.enabled=true"})
    @ContextConfiguration(classes = { AutoDisableRolesService.class})
    public class AutoDisableRolesServiceTests {
        
        @MockBean
        private UserRoleRepository userRoleRepository;
    
        @Autowired
        private AutoDisableRolesService autoDisableRolesService;
    
        // remainder of test class omitted
    }
    
    

    The difference between @SpringBootTest and @ExtendsWith(SpringExtension.class) is that a @SpringBootTest loads the full (test)ApplicationContext while the latter only loads a partial context, which is faster but doesn't include everything.