My team is currently building a SpringBoot API. Below is a snippet of how my team is mocking for our controller tests. This works when we have plain classes as dependencies.
#RunWith(SpringRunner.class)
#SpringBootTest
public class TestController {
private MockMvc mockMvc;
#Mock
private MyService myService;
#Autowired
#InjectMocks
private MyController myController;
#Before
public void setup() {
//Build the controller mock handler
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
We cannot figure out how to mock a #Resource like below for a similar test.
#Resource(name = "domainNameToCode")
private Map<String, Integer> domainCodes;
As the domainCodes is private your controller needs to have a way to set value for it either constructor or a setter.
Setter
#Autowired()
#Qualifier("domainNameToCode")
public void setDomainCodes(Map<String, Integer> domainCodes) {
...
}
Constructor
#Autowired()
#Qualifier("domainNameToCode")
public MyController(Map<String, Integer> domainCodes){
}
In your tests, now you can inject the mocks.
If you don't want to edit your source, then you can use reflection to set a private instance variable on an object
//Using reflection to set a private field on an object
FieldUtils.writeField(controllerInstance, "domainCodes", new HashMap<>()) ;
Related
I'm using spring boot rest API and want to test my service layer. In my service I have autowired few beans and its not through constructor. (I like it that way to keep it short).
In the junit, I have created mocks and for private field which I do want to execute, I have assigned using ReflectionTestUtils.setField. When I debug, the method inside the field is not getting executed which assigned by bean.
Service Class
#Component
public class MyService {
#Autowired
private MyRepository myRepository;
#Autowired
private ResponseMapper responseMapper;
public List<MyObj> getList(String param) throws MyException {
log.info("Getting details");
Optional<List<MyObj>> list = myRepository.findByParam(param);
List<MyObj> data = responseMapper.mapToResponseData(list);
return data;
}
}
Test Class
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private MyRepository myRepository;
#Mock
private ResponseMapper responseMapper;
#Before
public void setUp() {
ReflectionTestUtils.setField(myService, "responseMapper", responseMapper);
}
#Test
public void getListTest() throws Exception {
when(myRepository.findByParam(anyString()))
.thenReturn(Optional.of(getSampleList()));
List<MyObj> list = myService.getList("param");
assertTrue(list.size() >0);
}
}
This results in Assertion failure and when I debug, the method mapToResponseData in ResponseMapper is not getting executed.
I know I can mock mapToResponseData method also. But I wanted it to execute so I don't have to write another test class for mapper alone.
What am I doing wrong? Is this the right wat to inject bean? And is using constructor in service to inject beans only option?
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.
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
}
...
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()
If you are using #Mock , you need to stub the values as well. By default the value will be null for all the properties.
I've this constructor:
public class AuditsLoaderService {
public AuditsLoaderService(
BackOfficeProperties backOfficeProperties,
FrontOfficeInterfaceService frontOfficeService,
AuditService auditService,
I18nResourceBundleMessageSource messageSource,
MonitorService monitor
) {
// some code
}
}
and AuditService is:
public class AuditService {
public AuditService(
BackOfficeProperties backOfficeProperties,
AuditFactory auditFactory,
AuditDao auditDAO,
RedisDao auditRedisDao,
ObjectMapper objectMapper,
I18nResourceBundleMessageSource messageSource
) {
Into my test:
#RunWith(MockitoJUnitRunner.class)
public class MalformedAuditsTest {
#Mock
private BackOfficeProperties backOfficeProperties;
#Mock
private AuditFactory auditFactory;
#Mock
private AuditDao auditDao;
#Mock
private RedisDao redisDao;
#Mock
private ObjectMapper objectMapper;
#Mock
private I18nResourceBundleMessageSource i18nResourceBundleMessageSource;
#Mock
private MonitorService monitorService;
#InjectMocks
#Spy
private AuditService auditService;
#InjectMocks
private FrontOfficeInterfaceService frontOfficeService;
#InjectMocks
private AuditsLoaderService auditsLoaderService;
}
Nevertheless, I'm getting an NulPointerException inside my AuditsLoaderService since auditService is null.
It seems it's not injected.
I've annotated it as:
#InjectMocks
#Spy
private AuditService auditService;
Should I add some more code/annotation?
EDIT
I've just realized neither frontOfficeService neither auditSerice are injected on.
So, it seems all #InjectMocks fields are not injected on others ones
Try adding:
#Mock
private ObjectMapper objectMapper;
because your AuditService constructor requires it
EDIT: It's probably easier to create objects manually by passing mocks in corresponding constructor
Here is my class under test:
KafkaProcessorApplication
#EnableBinding(Processor.class)
#EnableConfigurationProperties(KafkaProperties.class)
public class KafkaProcessorApplication {
#Autowired
private Processor processor;
#Autowired
private KafkaProperties kafkaProperties;
private KafkaTemplate<String, String> kafkaTemplate;
#Autowired
KafkaProcessorApplication(SenderConfig senderConfig) {
this.kafkaTemplate = senderConfig.kafkaTemplate();
}
Here, SenderConfig is a just a simple config class with the method kafkaTemplate() creating a new instance of KafkaTemplate.
SenderConfig
#Configuration
public class SenderConfig {
#Autowired
KafkaProperties kafkaProperties;
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(new HashMap());
}
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(ProducerFactory()));
}
}
Here is the test class:
KafkaTestClass
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = {SenderConfig.class, KafkaProcessorApplication.class})
#TestPropertySource(locations = "classpath:test-resources.properties")
#RunWith(SpringRunner.class)
public class KafkaProcessorApplicationTest {
#Autowired
private Processor processor;
#Mock
private SenderConfig senderConfig;
#Mock
private KafkaProperties kafkaProperties = new KafkaProperties();
#Mock private KafkaTemplate mockKafka;
#Autowired
#InjectMocks
private KafkaProcessorApplication app;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("ServerConfig").when(kafkaProperties).getServersConfig();
when(senderConfig.kafkaTemplate()).thenReturn(kafkaTemplate);
}
I want to mock kafkaTemplate. But, its instantiation is in constructor which is being executed even before the #Before is executed, where the logic of mocking the method is written.
Just curious why is the constructor being executed first, and also, how can I mock the method if this is the case?
What could be the approaches of mocking the kafkaTemplate, without using Powermock and without modifying the class under test as I can not change it?
When you use #SpringBootTest, the Spring dependency tree is resolved before the #Before method has a chance to execute. This includes constructing the KafkaProcessorApplication bean and its dependencies. This is why the constructor runs before #Before.
What you want is Spring's #MockBean to create and inject a mock bean in the application context.
This question has a great write-up of how you can use this: Difference between #Mock, #MockBean and Mockito.mock()
update
Now I see. The problem is that the KafkaProcessorApplication accesses the mock in its constructor before you can configure it.
This can be solved with a separate test Spring configuration that will return a configured SenderConfig mock bean as described here: Testing spring bean with post construct
I have a Spring MVC REST controller class that has a private instance boolean field injected via #Value ,
#Value("${...property_name..}")
private boolean isFileIndex;
Now to unit test this controller class, I need to inject this boolean.
How do I do that with MockMvc?
I can use reflection but MockMvc instance doesn't give me underlying controller instance to pass to Field.setBoolean() method.
Test class runs without mocking or injecting this dependency with value always being false. I need to set it to true to cover all paths.
Set up looks like below.
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
....
}
You can use #TestPropertySource
#TestPropertySource(properties = {
"...property_name..=testValue",
})
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
}
You can also load your test properties form a file
#TestPropertySource(locations = "classpath:test.properties")
EDIT: Some other possible alternative
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private Controller controllerUnderTheTest;
#Test
public void test(){
ReflectionTestUtils.setField(controllerUnderTheTest, "isFileIndex", Boolean.TRUE);
//..
}
}
My preferred option would be to set it in the constructor and annotate the constructor parameter with the #Value. You could then pass in whatever you want in the test.
See this answer