How to test Kafka OnFailure callback with Junit? - junit

I have the following code to send data to Kafka:
#Service
public class KafkaSender{
#Autowired
private KafkaTemplate<String, Employee> kafkaTemplate;
public void sendMessage(Employee employee) {
ObjectMapper objectMapper = new ObjectMapper();
ListenableFuture<SendResult<String, Employee>> listenableFuture = kafkaTemplate.send(topic,employee);
listenableFuture.addCallback(new ListenableFutureCallback<SendResult<String, Employee>>() {
#Override
public void onSuccess(SendResult<String, Employee> result) {
// method to save in DB
saveInDatabaseMethod(result.getProducerRecord());
}
#Override
public void onFailure(Throwable ex) {
// class cast exception occur here
ProducerRecord<String, Employee> producerRecord = ((KafkaProducerException) ex).getFailedProducerRecord();
saveInDatabaseMethod(producerRecord);
}
}
}
}
I am able to test the OnSucess callback scenario, but i am not able to test the OnFailure one.
#Test
void test() throws InterruptedException, ExecutionException {
Throwable ex = mock(Throwable.class);
Employee employee = new Employee();
when(kafkaTemplate.send(null,employee )).thenReturn(responseFuture);
when(sendResult.getProducerRecord()).thenReturn(producerRecord);
when(producerRecord.value()).thenReturn(employee);
doAnswer(invocationOnMock -> {
ListenableFutureCallback<SendResult<String, Employee>> listenableFutureCallback = invocationOnMock.getArgument(0);
listenableFutureCallback.onFailure(ex);
return null;
}).when(responseFuture).addCallback(any(ListenableFutureCallback.class));
kafkaSender.sendMessage(employee);
}
The above test throws:
java.lang.ClassCastException:
org.mockito.codegen.Throwable$MockitoMock$2137573915 cannot be cast to
org.springframework.kafka.core.KafkaProducerException

ProducerRecord<String, Employee> producerRecord = ((KafkaProducerException) ex).getFailedProducerRecord();
Your mock is not calling the callback with a KPE, its calling it with this
Throwable ex = mock(Throwable.class);
You need to wrap it in a KPE.

Related

Mockito for SimpleJdbcCall with Spring JdbcTemplate

I tried multiple way to execute the store procedure in my Junit test case to test against the out values but unfortunately nothing is working.
My Test case:
public class DataTest {
#Mock
private static DataSource ds;
#InjectMocks
private DataDaoImpl dataDao = new DataDaoImpl();
#Mock
private static JdbcTemplate jdbcTemplate;
#Mock
private static SimpleJdbcCall viewProc;
#Before
public void setUp() throws IOException, InterruptedException {
MockitoAnnotations.initMocks(this);
MockMvcBuilders.standaloneSetup(dataDao).build();
}
#BeforeClass
public static void init() throws Exception {
viewProc = new SimpleJdbcCall(ds).withSchemaName("schema")
.withProcedureName("viewProc").withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("param1", Types.VARCHAR))
.declareParameters(new SqlParameter("param2", Types.VARCHAR))
.returningResultSet("dataModules", Mockito.anyObject());
jdbcTemplate = new JdbcTemplate(ds);
}
#Test
public void findDataModules() throws Exception {
String param1 = "abc";
List<DataObj> md = new ArrayList<DataObj>();
int size = 3;
SqlParameterSource in = new MapSqlParameterSource().addValue("param1", "abc").addValue("param2",
"123");
Map map = viewProc.execute(in);
md = (List<DataObj>) map.get("data");
assertTrue("Expected Data ", md.size() >= size);
}
}
My Main class:
#Repository
public class DataDaoImpl implements DataDao {
protected Logger logger = LoggerFactory.getLogger(getClass());
#Resource(name = "db")
private DataSource db;
private SimpleJdbcCall viewProc;
private JdbcTemplate jdbcTemplate;
/**
* Initialization of Stored Procs and JDBC Template
*
* #throws Exception
*/
#PostConstruct
public void init() throws Exception {
viewProc = new SimpleJdbcCall(db).withSchemaName("schema")
.withProcedureName("viewProc").withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("param1", Types.VARCHAR))
.declareParameters(new SqlParameter("param2", Types.VARCHAR))
.returningResultSet("data", new ViewDataRowMapper());
jdbcTemplate = new JdbcTemplate(db);
}
#Override
public List<Data> findUniqueDataModules(String p1, String p2) throws Exception {
List<DataObj> dataModules = new ArrayList<DataObj>();
try {
SqlParameterSource in = new MapSqlParameterSource().addValue("param1", p1).addValue("param2",
p2);
Map map = viewUniqueDataModulesByLicense.execute(in);
dataModules = (List<DataObj>) map.get("data");
} catch (Exception e) {
//Hnadel Exception
}
return dataModules;
}
}
Above code gives exception says datasource is required.
I tried Mockito, powerMockito but it returning empty map. There is no exceptions with mock.
I am OK with any solution which can pass my test case.
Modified naming.
As much as I hate using reflection in testing, I believe it can help you in your case. Here, after initializing, I set the field viewProc to a mock object which you can use in the test. #PostConstruct is a Spring related annotation, so it will not be called while initializing it.
class DataDaoImplTest {
private DataDaoImpl dataDao;
#Mock
private DataSource dataSource;
#Mock
private SimpleJdbcCall jdbcCall;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
this.dataDao = new DataDaoImpl(dataSource);
ReflectionTestUtils.setField(this.dataDao, "viewProc", jdbcCall);
}
#Test
void findUniqueDataModules() throws Exception {
// given:
Map<String, Object> map = new HashMap<>();
map.put("data", Arrays.asList(new DataDaoImpl.DataObj(), new DataDaoImpl.DataObj()));
// mocks:
when(jdbcCall.execute(any(SqlParameterSource.class))).thenReturn(map);
// when:
List<DataDaoImpl.DataObj> uniqueDataModules = this.dataDao.findUniqueDataModules("a", "b");
// then:
assertEquals(2, uniqueDataModules.size());
}
}
Another solution would be to test the method against a test database, like H2. But it won't be a unit test.

Deserialize kafka messages in KafkaConsumer using springboot

I have a springboot app that listen kafka messages and convert them to object
#KafkaListener(topics = "test", groupId = "group_id")
public void consume(String message) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Hostel hostel = objectMapper.readValue(message, Hostel.class);
}
I woder if it is possible to do ti directly
#KafkaListener(topics = "test", groupId = "group_id")
public void consume(Hostel hostel) throws IOException {
}
You can do it using spring-kafka. But then you need to use a custom deserializer (or a JsonDeserializer) in the container factory
#KafkaListener(topics = "test", groupId = "my.group", containerFactory = "myKafkaFactory")
fun genericMessageListener(myRequest: MyRequest, ack: Acknowledgment) {
//do Something with myRequest
ack.acknowledge()
}
Your ContainerFactory will look something like
#Bean
fun myKafkaFactory(): ConcurrentKafkaListenerContainerFactory<String, MyRequest> {
val factory = ConcurrentKafkaListenerContainerFactory<String, MyRequest>()
factory.consumerFactory = DefaultKafkaConsumerFactory(configProps(), StringDeserializer(), MyRequestDeserializer())
factory.containerProperties.ackMode = ContainerProperties.AckMode.MANUAL
return factory
}
Your Deserialiser will look like
public class MyRequestDeserializer implements Deserializer {
private static ObjectMapper objectMapper = new ObjectMapper();
#Override
public void configure(Map map, boolean b) {
}
#Override
public MyRequest deserialize(String arg0, byte[] msgBytes) {
try {
return objectMapper.readValue(new String(msgBytes), MyRequest.class);
} catch (IOException ex) {
log.warn("JSON parse/ mapping exception occurred. ", ex);
return new MyRequest();
}
}
#Override
public void close() {
log.debug("MyRequestDeserializer closed");
}
}
Alternatively, you can use the default JsonDeserializer as given in spring docs

Junit for Controller class

I have controller method and for it I am making Junit but getting Null pointer error when it calling a service method. I used power mock but still getting Null pointer.
method:
#RequestMapping(method = RequestMethod.GET, value = "/DSR.do")
public ModelAndView displayDataSourceReportPage(HttpServletRequest request,Model model) {
log.debug(" Inside displayDataSourceReportPage method ");
Map<String, Object> map = new HashMap<String, Object>();
try {
request.setAttribute(MENU_SELECTED, LABEL_MENU_SOURCEDATA);
request.setAttribute(SUB_MENU_SELECTED, LABEL_SUBMENU_DSR);
#SuppressWarnings("rawtypes")
List dataSource = dataSourceReportService.listDataSourceReportByCurrentRunInd("C");
map.put("dataSource", dataSource);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return new ModelAndView("DataSourceReport", "model", map);
}
test Method:
#InjectMocks
private DataSourceReportController dataSourceReportController;
#Mock
private DataSourceReportService dataSourceReportServiceImpl;
#InjectMocks
private DataSourceReportDAO dataSourceReportDAO = new DataSourceReportDAOImpl();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testdisplayDataSourceReportPage() throws Exception {
PowerMockito.mockStatic(DataSourceReport.class);
PowerMockito.mockStatic(HttpServletRequest.class);
PowerMockito.mockStatic(Model.class);
PowerMockito.mockStatic(DataSourceReportService.class);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Model model = Mockito.mock(Model.class);
dataSourceReportServiceImpl = PowerMockito.mock(DataSourceReportService.class);
DataSourceReport dataSourceReport = PowerMockito.mock(DataSourceReport.class);
dataSourceReport.setCurrentRunInd("abc");
dataSourceReport.setActualFileName("Somthing");
dataSourceReport.setFileCountId(3);
dataSourceReport.setFileId(4);
dataSourceReport.setRecCount(3);
List<DataSourceReport> list = new ArrayList<DataSourceReport>();
list.add(dataSourceReport);
String currentRunInd = "currentRunInd";
Object obj =getClass();
PowerMockito.when(dataSourceReportDAO.listDataSourceReportByCurrentRunInd(currentRunInd)).thenReturn(list);
DataSourceReportController ctrl = new DataSourceReportController();
ctrl.displayDataSourceReportPage(request, model);
}
getting Null at "dataSourceReportService.listDataSourceReportByCurrentRunInd("C");"
You need to have this in the test class
PowerMockito.when(dataSourceReportService.listDataSourceReportByCurrentRunInd("C")).thenReturn(list);
before calling
ctrl.displayDataSourceReportPage(request, model);
Thanks # Arthur Zagretdinov
I tried the below code and it worked.
private MockMvc mockMvc;
#Mock
private HttpServletRequest req;
#Mock
private DataSourceReportService dataSourceReportServiceImpl;
#InjectMocks
private DataSourceReportController controller;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void testdisplayDataSourceReportPage() throws Exception {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Model model = Mockito.mock(Model.class);
DataSourceReport dataSourceReport =Mockito.mock(DataSourceReport.class);;
dataSourceReport.setCurrentRunInd("abc");
dataSourceReport.setActualFileName("Somthing");
dataSourceReport.setFileCountId(3);
dataSourceReport.setFileId(4);
dataSourceReport.setRecCount(3);
List<DataSourceReport> list = new ArrayList<DataSourceReport>();
list.add(dataSourceReport);
ModelAndView modelView = controller.displayDataSourceReportPage(request, model);
modelView.addObject(dataSourceReport);
}

Adding session attribute with spring rest template

Following is my Test class which is responsible for testing POST method.
#RunWith(JUnitParamsRunner.class)
#ApplicationTest
#WithMockUser("mike")
public class AnalysisRecordParam {
ObjectMapper objectMapper=new ObjectMapper();
MockHttpSession session=new MockHttpSession();
#ClassRule
public static final SpringClassRule SCR = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
private RestTemplate restTemplate = new TestRestTemplate("operations", "operations");
#Test
#Parameters(method= "parametersForSavesCustomer")
public void savesCustomer(String name,AnalysisRecordTO analysisRecordTO) throws Exception{
AnalysisRecordTO result = restTemplate.postForObject("http://localhost:8080/api/v1/analysisrecord/", analysisRecordTO, AnalysisRecordTO.class);
assertNotEquals(null, result);
}
#SuppressWarnings("unused")
public Object[] parametersForSavesCustomer() {
Object[] array=null;
Properties analysisMap=new Properties();
try {
analysisMap.load(AbstractJobArgumentProvider.class.getResourceAsStream("/config/analysis-list.properties"));
Set<Object> set=analysisMap.keySet();
array=new Object[analysisMap.size()];
int i=0;
for(Object key:set){
Object[] arr=new Object[2];
arr[0]=key.toString();
String jsonFilePath=(String)analysisMap.get(key);
AnalysisRecordTO analysis=this.objectMapper.readValue(new File(jsonFilePath), AnalysisRecordTO.class);
arr[1]=analysis;
array[i]=arr;
i++;
}
} catch (IOException e) {
e.printStackTrace();
}
return array;
}
}
I have one interceptor which looks for CONSUMER_TYPE and CONSUMER_ID in session before forwarding the request.
Code for interceptor is as below :
public class ConsumerTypeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String method = request.getMethod();
HttpSession session = request.getSession(false);
if(!method.equals("OPTIONS") && (session == null || session.getAttribute("CONSUMER_TYPE") == null || session.getAttribute("CONSUMER_ID") == null)){
System.out.println("Invalid request");
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, "Invalid login token");
return false;
}
return true;
}
}
Using Rest template request is given properly but it is denied by interceptor.
I just wanted to know how to set these session attributes?

Junit not calling #ControllerAdvice on UsernameNotFoundException

I'm coding an integration Test for my Rest Application. I wrote a test to check accesDenied and it raises an UsernameNotFoundException, which it should, yet it does not follow the exception to the #ControllerAdvice class which returns a JSON.
The code works correctly in execution, returning the Json, and in other test cases like AuthenticationFailed - whcih is also handled by an exception -, the Json is return on running the test. The json is not return in this case, maybe because I have a custom UserDetailsService?
I've seen on the internet, others just test if exception was raised, and call the day. Yet I'd like the test to return same behaviour as execution - the Json. Is it possible? What am I missing?
I tried similar questions' answers but they didn't work, same behaviour was returned.
Any help would be much appreciated. Thx in advance,
Alfonso
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
#WebAppConfiguration
public class SecurityIntegrationTest {
private final String SECURED_URI = "/users/1";
private final String LOGIN_URI = "/login";
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy springSecurityFilter;
#Autowired
CustomUserDetailsService customUserDetailsService;
#Autowired
UserController userController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.addFilters(springSecurityFilter).alwaysDo(print()).build();
}
#Test
public void requiresAuthentication() throws Exception {
mockMvc.perform(
get(SECURED_URI).contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(status().isUnauthorized())
.andExpect(
content().contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(content().string(Jsons.AUTHENTICATION_REQUIRED));
}
#Test
public void authenticationFailed() throws Exception {
mockMvc.perform(formLogin())
.andExpect(status().isUnauthorized())
.andExpect(
content().contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(content().string(Jsons.AUTHENTICATION_FAILED));
}
#Test
public void authenticationSuccess() throws Exception {
mockMvc.perform(formLogin().user("Ikos").password("Ikos"))
.andExpect(status().isOk())
.andExpect(
content().contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(
content().string(
String.format(Jsons.LOGIN, "Ikos", "Ikos")));
}
#Test
public void accessGranted() throws Exception {
UserDetails user = customUserDetailsService.loadUserByUsername("Ikos");
mockMvc.perform(
get(SECURED_URI).with(user(user)).contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(status().isOk())
.andExpect(
content().contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(content().string(RestDataFixture.defaultUserJSON()));
}
#Test
public void accessDenied() throws Exception {
UserDetails user = customUserDetailsService.loadUserByUsername("Pedro");
mockMvc.perform(
get(SECURED_URI).with(user(user)).contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(status().isUnauthorized())
.andExpect(
content().contentType(
MediaType.valueOf(Constants.REST_TYPE)))
.andExpect(content().string(Jsons.AUTHENTICATION_REQUIRED));
}
}
#Configuration
#ComponentScan(basePackages = { "es.aekia.rest" })
#EnableWebMvc
public class TestConfig {
}
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user;
try {
user = userDao.findByAlias(username);
if (user == null)
throw new UsernameNotFoundException("user name not found");
} catch (DataAccessException e) {
throw new UsernameNotFoundException("database error");
}
return buildUserFromUserEntity(user);
}
private UserDetails buildUserFromUserEntity(User userEntity) {
// convert model user to spring security user
String username = userEntity.getAlias();
String password = userEntity.getPassword();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_"
+ userEntity.getRole());
authorities.add(authority);
UserDetails springUser = new org.springframework.security.core.userdetails.User(
username, password, authorities);
return springUser;
}
}
#ControllerAdvice
public class ExceptionController {
#RequestMapping(produces = { Constants.REST_TYPE })
#ExceptionHandler({ MissingServletRequestParameterException.class,
UnsatisfiedServletRequestParameterException.class,
HttpRequestMethodNotSupportedException.class,
ServletRequestBindingException.class,
MethodArgumentNotValidException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public #ResponseBody Map<String, Object> handleRequestException(Exception ex) {
Map<String, Object> map = Maps.newHashMap();
map.put(Constants.ERROR, Constants.REQUEST_ERROR);
map.put(Constants.CAUSE, ex.getMessage());
return map;
}
#RequestMapping(produces = { Constants.REST_TYPE })
#ExceptionHandler(HttpMediaTypeNotSupportedException.class)
#ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public #ResponseBody Map<String, Object> handleUnsupportedMediaTypeException(
HttpMediaTypeNotSupportedException ex) throws IOException {
Map<String, Object> map = Maps.newHashMap();
map.put(Constants.ERROR, Constants.UNSUPPORTED_MEDIA_TYPE);
map.put(Constants.CAUSE, ex.getLocalizedMessage());
map.put(Constants.SUPPORTED, ex.getSupportedMediaTypes());
return map;
}
#RequestMapping(produces = { Constants.REST_TYPE })
#ExceptionHandler({ AccessDeniedException.class,
UsernameNotFoundException.class })
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public #ResponseBody Map<String, Object> handleAccesDeniedException(
Exception ex) {
Map<String, Object> map = Maps.newHashMap();
map.put(Constants.ERROR, Constants.ACCESS_DENIED);
map.put(Constants.CAUSE, ex.getMessage());
return map;
}
#RequestMapping(produces = { Constants.REST_TYPE })
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public #ResponseBody Map<String, Object> handleUncaughtException(
Exception ex) throws IOException {
Map<String, Object> map = Maps.newHashMap();
map.put(Constants.ERROR, Constants.UNKNOWN_ERROR);
if (ex.getCause() != null) {
map.put(Constants.CAUSE, ex.getCause().getMessage());
} else {
map.put(Constants.CAUSE, ex.getMessage());
}
return map;
}
}
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private RestAccessDeniedHandler restAccessDeniedHandler;
#Autowired
private RestAuthSuccessHandler restAuthSuccessHandler;
#Autowired
private RestAuthFailureHandler restAuthFailureHandler;
#Autowired
private RestLogoutSuccessHandler restLogoutSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
/*
* .authenticationProvider(authenticationProvider())
*/
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.accessDeniedHandler(restAccessDeniedHandler)
.and()
.formLogin()
.permitAll()
.loginProcessingUrl("/login")
// .usernameParameter(USERNAME)
// .passwordParameter(PASSWORD)
.successHandler(restAuthSuccessHandler)
.failureHandler(restAuthFailureHandler).and()
.logout()
.permitAll()
// .logoutRequestMatcher(new AntPathRequestMatcher(LOGIN_PATH,
// "DELETE"))
.logoutSuccessHandler(restLogoutSuccessHandler).and()
.sessionManagement().maximumSessions(1);
// .logoutSuccessUrl("/logout").and()
/*
* .sessionManagement() .sessionCreationPolicy (SessionCreationPolicy
* .STATELESS).and()
*/
//
http.authorizeRequests().antMatchers(HttpMethod.POST, "/login")
.permitAll().antMatchers(HttpMethod.POST, "/logout")
.authenticated().antMatchers(HttpMethod.GET, "/users")
.permitAll().antMatchers(HttpMethod.GET, "/users/**")
.hasAnyRole("USER", "ADMIN")
.antMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN");
// .anyRequest().anonymous();
}
}