Why is the constructor method being called before setup - java

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

Related

Problem with #Cachable and #Mock/#InjectMocks

I'm currently unable to use #Cacheble and #Mock/#InjectMocks in the same test class.
To be clear, for using the Mockito.verify(repository, times(1)) I need to #Mock the repository and use the annotation #InjectMock for the repository. Doing it, I can correctly verify the behave of my app, but the result of findById is not cached. In fact the manager.get(cacheName).get(key) will return null.
Instead, if i use the #Autowired annotation, the value is cached but the verify(repository, times(1)) return ZeroInteractions. I checked with the debugger and the behaviour is ok.
What i should do for have both the cache-store and the verify() working? Why is that happening?
[I'm using SpringBoot 2.1.3-release with Caffeine Cache Manager version 3.1.1]
This is the CacheManager class:
#Slf4j
#Configuration
#EnableCaching
public class CaffeineCacheConfig extends CachingConfigurerSupport {
#Bean("cacheManager")
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(generateCache());
return cacheManager;
}
}
This the Cache:
#Override
public CaffeineCache generateCache() {
return new CaffeineCache(name,
Caffeine.newBuilder()
.maximumSize(15)
.expireAfterWrite(60, TimeUnit.MINUTES)
.recordStats()
.build());
}
I have a service that im trying to cache which is like:
#Service
#CacheConfig(cacheNames = CacheLocations.APPLICATION_LEVEL)
#Transactional(readOnly = true)
public class ApplicationLevelService implements IApplicationLevelService
{
#Autowired
private ApplicationLevelRepository repository;
#Autowired
private CacheManager manager;
#Override
#Cacheable
public ApplicationLevel findById(int id)
{
return repository.findById(id);
}
}
Im trying to test it with this class (JUnit5):
#CacheConfig(cacheNames = {APPLICATION_LEVEL})
class ApplicationLevelCacheTest extends AbstractSpringTest
{
#InjectMocks
private ApplicationLevelService service;
#Mock
private ApplicationLevelRepository repository;
#Autowired
private CacheManager cacheManager;
#BeforeEach
void evictCache()
{
cacheManager.getCache(APPLICATION_LEVEL).clear();
}
#Nested
class TestApplicationLevelCache
{
#Test
#DisplayName("findAById() sets the resulting list in '" + CacheLocations.APPLICATION_LEVEL + "' cache")
void testApplicationLevelCaching_ApplicationLevelsAreCached_FindById()
{
when(repository.findById(anyInt())).thenReturn(Optional.of(new ApplicationLevel("mocked")));
assertNotNull(cacheManager.getCache(CacheLocations.APPLICATION_LEVEL));
var expected = service.findById(1);
verify(repository, times(1)).findById(anyInt());
assertNotNull(cacheManager.getCache(APPLICATION_LEVEL).get(1));
// should be cached
var actual = service.findById(1);
verifyZeroInteractions(repository);
assertEquals(expected, actual);
}
}
}
where AbstractSpringTest is just the class containing:
#SpringJUnitConfig
#SpringBootTest()
#ActiveProfiles("test")
Testing #Cacheable requires #SpringBootTest to bootstrap the entire container. This means this is an integration test you wrote, going through many layers of the application.
In this case, prefer #Autowired/#MockBean to #InjectMock/#Mock (you can use keep #InjectMock/#Mock for unit tests).
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
Try replacing:
#InjectMocks ApplicationLevelService service;
#Autowired CacheManager cacheManager;
#Mock ApplicationLevelRepository repository;
With:
#Autowired ApplicationLevelService service;
#Autowired CacheManager cacheManager;
#MockBean ApplicationLevelRepository repository;
The verify() method can be used to verify the number of interactions of a bean that has been mocked with Mockito.
Using the #Autowired annotation you have initialized the bean by taking it directly from the Spring container, using Spring's D.I.
Your cacheManager object is outside the context of Mockito.
It has not been mocked with Mockito.
I think you can solve this problem by using the #Spy annotation on the cacheManager object.
#Spy and #Mock can be both used to mock fields.
The difference is that with the #Mock annotation, you create a complete mock or dummy object, whereas with #Spy, you have the real object and can directly call or mock specific methods of it.
You can try with:
#Spy
CacheManager cacheManager = new SimpleCacheManager();
Hope this help.

Junit Mocked bean is not executing

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?

Can I inject mocks into a prototype bean with Autowired constructor?

Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}

Mocking #Resource when using MockMvcBuilders.standaloneSetup

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<>()) ;

Is it possible to intercept JUnit test methods?

I would like to intercept JUnit test methods. I already have interceptor that prepares context for the test. I don't want to duplicate the code in my interceptor.
Is it possible to tell SpringJUnit4ClassRunner to use myTest() instance of the test class? Something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyConfig.class })
public class MyTest {
#Autowired
private MyInterceptor myInterceptor;
#Autowired
private BeanFactory beanFactory;
#Bean
public MyTest myTest() {
final ProxyFactoryBean proxy = new ProxyFactoryBean();
proxy.setBeanFactory(this.beanFactory);
proxy.setTarget(this);
proxy.addAdvice(this.myInterceptor);
return (MyTest) proxy.getObject();
}
}

Categories

Resources