Mock Class instance in java - junit

Is there a way to mock Class instance in java
#Autowired
private ApplicationContext context;
public <T> T search(Class<? extends JpaSpecificationExecutor<ENTITY>>
specExecutor,
Class<? extends Converter<DTO, ENTITY>> objConverter) {
JpaSpecificationExecutor jpaSpecificationExecutor = context.getBean(specExecutor);
Converter converter = context.getBean(objConverter);
}
I have to unit test the above method, i am using Mockito.
The issue is, when i use:
Mockito.when(context.getBean(any(Class.class))).thenReturn(new MyJpaSpecificationExecutor());
The above will not work for context.getBean(objConverter); // ClassCastException
Is there a way in Mockito or PowerMockito to achieve this?

Related

autowired ObjectMapper is null during #DataJpaTest

I want to test my implementation for AttributeConverter using #DataJpaTest.
test code
#RunWith(SpringRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class FooRepositoryTest {
#Autowired
private FooRepository repository;
#Test
void getPojoTest(){
FooEntity fooEnity= repository.findById("foo");
FooPojo fooPojo = fooEntity.getJsonPojo()
//some assertion
}
}
Entity
#Entity
#Data
#NoArgsConstructor
public class FooEntity{
....
#Column(columnDefinition= "JSON")
#Convert(converter = FooConverter.class)
private FooPojo data;
....
}
Attribute Converter
public class FooConverter implements AttributeConverter<FooPojo, String> {
#Autowired
private ObjectMapper mapper;
#SneakyThrows
#Override
public String convertToDatabaseColumn(FooPojo attribute) {
return mapper.writeValueAsString(attribute);
}
#SneakyThrows
#Override
public FooPojo convertToEntityAttribute(String dbData) {
return mapper.readValue(dbData, FooPojo.class);
}
}
with my code above, when I run getPojoTest(), the #autowired OjbectMapper in Converter is null. When I try the same test with #SpringBootTest instead, it works just fine. I wonder is there any walk-around to use #DataJpaTest and ObjectMapper together.
A better alternative compared to creating your own ObjectMapper is adding the #AutoConfigureJson annotation:
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#AutoConfigureJson
public void FooRepositoryTest {
}
This is also what #JsonTest uses.
From Docs:
#DataJpaTest can be used if you want to test JPA applications. By
default it will configure an in-memory embedded database, scan for
#Entity classes and configure Spring Data JPA repositories. Regular
#Component beans will not be loaded into the ApplicationContext.

Junit Mocking throwing Null pointer

I'm trying to mock the below service class.
I have created a separate Config bean which I'm calling as you can see below in the code:-
#Component
public class CrawlerService implements CrawlerServiceInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(CrawlerService.class);
private final CrawlConfig config;
#Autowired
CrawlerFactory crawlerFactory;
#Autowired
public CrawlerService(#Qualifier(value = "CrawlerConfig") CrawlConfig config) {
this.config = config;
}
#Value("${com.prudential.noOfCrawlers}")
private int numberOfCrawlers;
#Override
public Object crawlService(String URL, int max) throws Exception {
LOGGER.info("In Service Class");
this.config.setMaxPagesToFetch(max);
PageFetcher pageFetcher = new PageFetcher(config);
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);
CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);
controller.addSeed(URL);
controller.start(crawlerFactory, numberOfCrawlers);
List<Object> crawlersLocalData = controller.getCrawlersLocalData();
LOGGER.info("End of Service Class");
return crawlersLocalData;
}
}
The problem is that I'm getting a null pointer exception in this.config.setMaxPagesToFetch.
What am I doing wrong?
If I mocked some dependent object with #Mock annotation, does it mean it won't allow the value to be changed?
Here is the test class:-
List<Object> obj=new ArrayList<Object>();
#Mock
private CrawlConfig config;
#Mock
CrawlerFactory crawlerFactory;
#Mock
CrawlController controller;
#InjectMocks
CrawlerService crawlerService = new CrawlerService(config);
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testService() throws Exception {
when(controller.getCrawlersLocalData()).thenReturn(obj);
assertEquals(crawlerService.crawlService("https://vogella.com", 10), obj);
}
Here you have the problem with mock creating order. First of all, let's see what is happening here:
First, you are declaring some fields. In this case, you are declaring private CrawlConfig config and CrawlerService crawlerService. But crawlerService you don't just declare but initialize as well.
When you initialize it, you are passing as a dependency to it CrawlConfig config which is null.
Then MockitoAnnotations.initMocks(this) start doing it's magic. What does it do? It initializes all the fields annotated with #Mock, then it tries to initialize the #InjectMocks if it's not already initialized. In your case it is already initialized, so mockito won't initialize it and will use the instance which you created.
After that the mock injection takes part. Mockito will try to use any of it's strategy to inject mocks. You can read more about it in #InjectMocks javadoc. So in your case, mockito will try to use property setter or field injection. However, the CrawlConfig config in your CrawlerService is declared final, and mockito will ignore this field, as for this type of injection mockito ignores final or static fields.
So here you have created an instance of CrawlerService with dependency CrawlConfig config which is null and mockito won't do anything about it.
1) You can mock the CrawlConfig config by yourself before mockito is going to:
private CrawlConfig config = Mockito.mock(CrawlConfig.class);
#InjectMocks
CrawlerService crawlerService = new CrawlerService(config);
2) You can omit the initialization of the CrawlerService and rely on mockito:
#Mock
private CrawlConfig config = Mockito.mock(CrawlConfig.class);
#InjectMocks
CrawlerService crawlerService;
Each of these methods should work.
You can as well remove the final declaration from your CrawlerService and it will also work. But as for me, the two methods above are better options.

How do I autowire a field with a real implementation while mocking another field?

I'm trying to unit test a Spring-boot controller and one of my #Autowired fields is coming back null.
I have two autowired fields in this controller:
public class UserProfileController{
#Autowired
private UserProfileService profileService;
#Autowired
private IDataValidator dataValidatorImpl;
My test class is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
#Mock
UserProfileService profileServiceMock;
#Autowired
ApplicationContext actx;
#InjectMocks
private UserProfileController profileController;
#Before
public void setup() {
// Process mock annotations
String[] asdf = actx.getBeanDefinitionNames();
for (int i = 0; i < asdf.length; i++){
System.out.println(asdf[i]);
}
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(profileController).build();
}
/**
* All this does is verify that we return the correct datatype and HTTP status
* #throws Exception
*/
#Test
public void testGetProfileSuccess() throws Exception {
Mockito.when(profileServiceMock.getProfile(Mockito.any(HashMap.class))).thenReturn(new HashMap<String, Object>());
mockMvc.perform(get("http://localhost:8095/UserName?tenantId=tenant1"))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8));
//verify profileService was only used once
Mockito.verify(profileServiceMock, Mockito.times(1)).getProfile(Mockito.any(HashMap.class));
//verify we're done interacting with profile service
Mockito.verifyNoMoreInteractions(profileServiceMock);
}
If I leave IDataValidator untouched in the test class, it comes up null and I get a NPE. If I #Spy the DataValidatorImpl, it cannot find properties from the Spring environment that it needs to work.
How can I just let the IDataValidator autowire itself and maintain its spring environment context as if I were just running the application normally?
When I print all beans in my #Before setup() method, I can see DataValidationImpl in the list.
When you mock your controller with
MockMvcBuilders.standaloneSetup(profileController).build();
the controller is replaced in the context. Since you did not inject any IDataValidator in it, it is null.
The simplest solution is to autowired the real IDataValidator into your test class and inject it into the controller.
In your controller:
public class UserProfileController{
private UserProfileService profileService;
private IDataValidator dataValidatorImpl;
#Autowired
public UserProfileController(UserProfileService profileService, IDataValidator dataValidatorImpl) {
this.profileService = profileService;
this.dataValidatorImpl = dataValidatorImpl;
}
And in your test :
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
private UserProfileService profileService;
#Autowired
private IDataValidator dataValidator;
#Before
public void setup() {
UserProfileService profileService = Mockito.mock(UserProfileService.class);
UserProfileController controller = new UserProfileController(profileService, dataValidator);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
}
If I understand correctly, you want to inject UserProfileController with real Validator and mock Service.
In this case I suggest to use #ContextConfiguration annotaion which allows to configure context in the test. You'll need to create a Configuration class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(classes = UserProfileServiceApplication.class)
public class ControllerTest {
private MockMvc mockMvc;
#Mock
UserProfileService profileServiceMock;
#Autowired
ApplicationContext actx;
//comment this line out
//#InjectMocks
#Autowired
private UserProfileController profileController;
#Before
public void setup() {
// Process mock annotations
String[] asdf = actx.getBeanDefinitionNames();
for (int i = 0; i < asdf.length; i++){
System.out.println(asdf[i]);
}
//comment this line out
//MockitoAnnotations.initMocks(this);
#Configuration
public static class Config {
//wire validator - if it is not wired by other configurations
#Bean
Validator validator() {
return new Validaor();
}
//wire mock service
#Bean
public UserProfileService profileService() {
return mock(UserProfileService.class);
}
}
Okay, I swear I did this the first time but when trying to recreate the error thrown for jny it actually worked.
My solution is to inject via #Spy annotation and get the bean from the ApplicationContext in my #Before setup method.
public class ControllerTest {
private MockMvc mockMvc;
#Mock
UserProfileService profileServiceMock;
#Spy
IDataValidator dataValidator;
#Autowired
ApplicationContext actx;
#InjectMocks
private UserProfileController profileController;
#Before
public void setup() {
dataValidator = (IDataValidator) actx.getBean("dataValidatorImpl");
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(profileController).build();
}

JDBI mapper JUnit tests

I'd like to unit test my JDBI mapper classes since not all do trivial property mapping.
My testing class looks as follows:
public class IdentRuleMapperTest {
#Mock
ResultSet resultSet;
#Mock
ResultSetMetaData resultSetMetaData;
#Mock
StatementContext ctx;
IdentRuleMapper mapper;
#Before
public void setup() {
mapper = new IdentRuleMapper();
}
#Test
public void mapTest() throws SQLException {
Mockito.when(resultSet.getString("ID")).thenReturn("The ID");
Mockito.when(resultSet.getString("NAME")).thenReturn("The name");
Mockito.when(resultSet.getString("REGULATION")).thenReturn("CRS");
Mockito.when(resultSet.getString("JSON_ACTIONS_STRING")).thenReturn("the json string");
IdentRule identRule = mapper.map(0, resultSet, ctx);
}
}
The test throws NPE on the line
Mockito.when(resultSet.getString("ID")).thenReturn("The ID");
Anyone can point out to me why this won't work?
The annotation #Mock does not create the mock objects by itself. You have to add Mockito's JUnit rule as a field to your test
#Rule
public MockitoRule rule = MockitoJUnit.rule();
or use its JUnit runner
#RunWith(MockitoJUnitRunner.class)
public class IdentRuleMapperTest {
...
or create the mocks in an #Before method using MockitoAnnotations
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
when setting the expectations on mock objects, use Matchers for arguments matching.
Mockito.when(resultSet.getString( Matchers.eq("ID"))).thenReturn("The ID");

Using HttpMessageConverter causes HTTP 406

After extensive investigations, I wanted to share the problem and the resolution.
Problem
I have a RestController that works well, as long as I'm in charge of converting the JSON message. The moment I try to use an HttpMessageConverter to make the conversion more elegant, the client will start receiving HTTP 406.
So this works:
#RequestMapping(value = "/objects", method = RequestMethod.GET)
public Map<String, Object>[] getObjects(#RequestBody Object jsonQuery) {
MyQuery query = new MyConverter().convert(jsonQuery);
// do something with query
}
But, when I configure the converter, like this:
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> httpMessageConverters) {
httpMessageConverters.add(new QueryMessageConverter(new MediaType("application", "json")));
}
}
This causes HTTP 406:
#RequestMapping(value = "/objects", method = RequestMethod.GET)
public Map<String, Object>[] getObjects(#RequestBody Query Query) {
// do something with query
}
My pom.xml only refers spring-boot, and doesn't mention jackson at all.
Solution
See below
The solution is really very simple, and it is to register the jackson handler explicitly:
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> httpMessageConverters) {
httpMessageConverters.add(new QueryMessageConverter(new MediaType("application", "json")));
httpMessageConverters.add(new MappingJackson2HttpMessageConverter());
}
}