How come my Test fails in Docker Maven container 3.5.4-jdk8 when it passes using maven on command line - maven

I have the following test which passes in IDE, and when run as Junit and even from the command line using mvn clean verify.
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
#RunWith(SpringRunner.class)
#WebFluxTest(controllers = AddNewEntryController.class)
#Import({ThymeleafAutoConfiguration.class})
public class AddNewEntryControllerTest {
#Autowired
WebTestClient webTestClient;
#MockBean
TimeKeepingEntryService service;
#Captor
private ArgumentCaptor<Flux<TimeKeepingEntry>> captor;
#Autowired
AddNewEntryController controller;
LocalDateTime now = LocalDateTime.now();
String month = now.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH);
TimeKeepingEntry entry1 = new TimeKeepingEntry(month,
now.getDayOfMonth(), now.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH), now.toLocalTime(),
LocalTime.parse("00:30", DateTimeFormatter.ofPattern("HH:mm")),
now.toLocalTime().plusHours(7), "7.5", false );
#Test
public void addNewEntryPage() {
EntityExchangeResult<String> result = webTestClient
.get().uri("/add-new-entry")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).returnResult();
assertThat(result.getResponseBody())
.contains("<title>Add New Time Entry</title>")
.contains("<input type=\"text\" class=\"form-control\" readonly=\"readonly\" id=\"month\" name=\"month\" value=\"" + month + "\">")
.contains("<input data-date-format=\"dd\" id=\"datepicker\" name=\"dateOfMonth\" value=\"0\">")
.contains("<input type=\"text\" class=\"form-control\" id=\"day\" name=\"day\" value=\"" + now.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + "\">");
}
#Test
public void addNewEntrySubmit() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("month", month);
formData.add("dateOfMonth", Integer.toString(21));
formData.add("day", "Tuesday");
formData.add("startTime", "09:00");
formData.add("endTime", "17:00");
formData.add("breakLength", "00:30");
formData.add("onsite", Boolean.toString(false));
given(service.addTimeKeepingEntry(any())).willReturn(Mono.empty());
webTestClient.post().uri("/add-new-entry").accept(MediaType.TEXT_HTML).contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/");
verify(service).addTimeKeepingEntry(captor.capture());
TimeKeepingEntry timeKeepingEntry = captor.getValue().blockFirst();
assertThat(timeKeepingEntry.getMonth()).isEqualTo(month);
}
}
However when I push the code to Gitlab.com and the CI takes over and uses the maven docker container 3.5.4-jdk8 it fails with the following error.
Field error in object 'timeKeepingEntry' on field 'startTime': rejected `value [09:00]; codes typeMismatch.timeKeepingEntry.startTime,typeMismatch.startTime,typeMismatch.java.time.LocalTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [timeKeepingEntry.startTime,startTime]; arguments []; default message [startTime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalTime' for property 'startTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#javax.validation.constraints.NotNull java.time.LocalTime] for value '09:00'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [09:00]]]`

Related

How to write test cases for custom ErrorAttributes in spring boot

I have updated the spring boot version to 2.6.4 and related other dependencies and got error in getErrorAttributes() method because of changes in its 2nd arguments type from Boolean to ErrorAttributeOptions
Custom ErrorAtttributes class:
#Component
class CustomErrorAttributes<T : Throwable> :DefaultErrorAttributes() {
override fun getErrorAttributes( request: ServerRequest , options: ErrorAttributeOptions ): MutableMap<String, Any> { // changes made here in 2nd parameter
val errorAttributes = super.getErrorAttributes(request, options) // throwing exception here
val status = (errorAttributes as MutableMap<String,Any>).getOrDefault(STATUS_KEY,null)
if(status != null && status as Int == HttpStatus.INTERNAL_SERVER_ERROR.value()){
errorAttributes.replace(MESSAGE_KEY, INTERNAL_SERVER_ERROR_MESSAGE)
}
return errorAttributes
}
}
Test method
private val internalError = "An unexpected error occurred"
#Mock private lateinit var request : ServerRequest
#Test
fun `For Internal Error`(){
var result : MutableMap<String,Any> = customErrorAttributes.getErrorAttributes(request, options) // It was working earlier version as we pass false in 2nd arguments
assertThat(result["message"]).isEqualTo(internalError)
}

Errors: UnfinishedStubbing

I am writing Junit test case and I want to mock KafkaTemplate method kafkaTemplate.send(TOPIC_NAME, "someData");. In my project, I am using spring boot and Kafka.
Below is the StudentRecords class. I am using mockito for mocking the dependencies.
#Component
public class StudentRecords {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#Value("${topicNameForStudents}")
private String TOPIC_NAME;
public String sendStudentData(StudentDTO studentDTO) {
String studentStr = null;
try {
if(null == studentDTO) {
throw new StudentException("studentDTO Object cant be null");
}
if(studentDTO.getId() == null) {
throw new StudentException("Id cant be empty");
}
ObjectMapper mapper = new ObjectMapper();
studentStr = mapper.writeValueAsString(srvgExecution);
kafkaTemplate.send(TOPIC_NAME, studentStr);
return "SUCCESS";
} catch (JsonProcessingException e) {
e.printStackTrace();
return "ERROR";
}
}
}
And test class is as follows:
#ExtendWith({ SpringExtension.class, MockitoExtension.class })
class StudentRecordsTest {
#InjectMocks
StudentRecords studentRec;
#Mock
private KafkaTemplate<String, String> kafkaTemplate;
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
}
}
And I getting the following error
[ERROR] Errors:
[ERROR] studentRec.testSendStudentData: ยป UnfinishedStubbing
It is happening at line studentRec.sendStudentData(studentDTO);
How I can resolve/write the junit for this?
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
}
after updating the junit to above one, ended up with below error at this statement Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
Argument(s) are different! Wanted:
kafkaTemplate.send(
<any string>,
<any string>
);
Your mock statement is incomplete.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
KafkaTemplate's send method returns a ListenableFuture object and hence you need to have your mock return it.
I understand, you are not really using the returned value as part of your code.
In that case you may simply return null as below.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
Although, I would suggest you should ideally check for return value for better error handling, but that can be a different story altogether.
In case you do plan to handle error by checking the return value, your above mock statement can be written to return both success and fail cases.
You may check below thread for more details on how to set the correct mock expectations for KafkaTemplate.
How to mock result from KafkaTemplate

Unusual exception.Could not obtain transaction-synchronized Session for current thread

I met this exception with springboot 1.5.3 + hibernate5.
And ,i used the #EnableTransactionManagement at the start application class. Have the transaction aop configuration as follow code...
#Aspect
#Configuration
public class TransactionManagerConfig {
private static final String AOP_POINTCUT_EXPRESSION="execution (* *..service..*Service*.*(..))";
#Autowired
#Qualifier("transactionManager")
private HibernateTransactionManager transactionManager;
......
......
It worked very nice until yestardy.And then,i create a service named c.a.b.service.MessageCoreService.
#Service
public class MessageCoreService {
#Autowired
private MessageSourceResolverMapper mapper;
#Autowired
private TenantIdentifierResolver tir;
public Map<String, MsgDetail> fetchCode(String code, Locale locale) {
System.err.println("Thread.currentThread().getName() at MessageCoreService:" + Thread.currentThread().getName());
System.err.println("request teanantId:" + tir.resolveCurrentTenantIdentifier());
System.err.println("locale3 at MessageCoreService:" + locale.toString());
MsgDetail enMsg = mapper.resolveCode(code, MessageSourceResolver.DEFAULT_LOCALE);
MsgDetail localeMsg = mapper.resolveCode(code, locale.toString());
Map<String, MsgDetail> ret = new HashMap<String, MsgDetail>();
ret.put("defaultLocale", enMsg);
ret.put("requestLocale", localeMsg);
return ret;
}
In the controller, do a test first.
#Autowired
private MessageCoreService msgService;
#RequestMapping("/test")
#ResponseBody
public Map<String, MsgDetail> test() {
System.err.println("111111111111111111111111111111111:");
TenantIdentifierResolver.tempOverideTenantId(ZAppUtils.getInitDatabaseId()); // change to init database
ServiceResult<Module> rslt = new ServiceResult<Module>();
Locale locale = new Locale("en", "US");
System.err.println("Thread.currentThread().getName() at ResourcesModuleController:" + Thread.currentThread().getName());
System.err.println("request teanantId:" + tir.resolveCurrentTenantIdentifier());
return msgService.fetchCode("e90001.5", locale);
Then, the hibernate exception "Could not obtain transaction-synchronized Session for current thread" occured.
I checked over and over,everything is ok.But it's aways occured.
I don't understand any of this. And then,i coped c.a.b.service.MessageCoreService. to c.a.c.service.MessageAdminService. Yes,just change a direcory,rename it.
In the controller,add a method.
#Autowired
private MessageCoreAdminService msgAdminService;
#RequestMapping("/test2")
#ResponseBody
public Map<String, MsgDetail> test2() {
System.err.println("111111111111111111111111111111111:");
TenantIdentifierResolver.tempOverideTenantId(ZAppUtils.getInitDatabaseId()); // change to init database
ServiceResult<Module> rslt = new ServiceResult<Module>();
Locale locale = new Locale("en", "US");
System.err.println("Thread.currentThread().getName() at ResourcesModuleController:" + Thread.currentThread().getName());
return msgAdminService.fetchCode("e90001.5", locale);
}
And then ,an incredible scene appeared. When i access the test2,it worked normally, but the test method ,it still show me the "transaction-synchronized Session" exception. Oh ,I feel like I'm breaking down. I can't resolve the problem.
Pls help.

Testing a REST endpoint with Spring, MongoDB using ObjectIds

I'm new to MongoDB and I'm writing a series of unit tests for a Mongo-backed REST web-service. Here's a simple test for a /clients/{id} enpoint :
#RunWith(MockitoJUnitRunner.class)
public class ClientsControllerMockMvcStandaloneTest {
private MockMvc mvc;
#Mock
private ClientsRepository clientsRepository;
#Mock
private ModelMapper modelMapper;
#InjectMocks
private ClientsController clientsController;
private ExceptionHandlerExceptionResolver createExceptionResolver() {
ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#SuppressWarnings("ConstantConditions")
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
final Method method = new ExceptionHandlerMethodResolver(RestResponseEntityExceptionHandler.class)
.resolveMethod(exception);
final RestResponseEntityExceptionHandler handler = new RestResponseEntityExceptionHandler();
return new ServletInvocableHandlerMethod(handler, method);
}
};
exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
#Before
public void setup() {
JacksonTester.initFields(this, new ObjectMapper());
mvc = MockMvcBuilders.standaloneSetup(clientsController)
.setHandlerExceptionResolvers(createExceptionResolver())
.build();
}
// GET /api/clients/{id} 200
#Test
public void findById_ClientEntryFound_ShouldReturnFoundClientEntry() throws Exception {
final ObjectId id = new ObjectId();
final Client client = Client.builder()
.id(id)
.name("Microsoft")
.build();
final ClientDTO clientDTO = ClientDTO.builder()
.id(id)
.name("Microsoft")
.build();
when(clientsRepository.findById(id))
.thenReturn(Optional.of(client));
when(modelMapper.map(client, ClientDTO.class))
.thenReturn(clientDTO);
mvc.perform(get("/clients/" + id.toString())
.accept(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", is(id)))
.andExpect(jsonPath("$.name", is("Microsoft")))
.andDo(MockMvcResultHandlers.print());
verify(modelMapper, times(1)).map(client, ClientDTO.class);
verify(clientsRepository, times(1)).findById(id);
verifyNoMoreInteractions(clientsRepository);
}
}
I expect this to work but I'm getting the following :
java.lang.AssertionError: JSON path "$.id"
Expected: is <5c9b9a0289d2b311b150b92c>
but: was <{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
Expected :is <5c9b9a0289d2b311b150b92c>
Actual :<{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
<Click to see difference>
Any help would be appreciated (including any pointers if you think my general approach could be improved!).
Cheers!
Jackson doesn't know your ObjectId instance should be serialized as 5c9b9a0289d2b311b150b92c and not as:
{
"timestamp": 1553701378,
"machineIdentifier": 9032371,
"processIdentifier": 4529,
"counter": 5290284,
"time": 1553701378000,
"date": 1553701378000,
"timeSecond": 1553701378
}
Luckily it's easy to fix. The ObjectId#toString() method (which will internally invoke ObjectId#toHexString()) allows you to convert the ObjectId instance into a 24-byte hexadecimal string representation.
So you could use #JsonSerialize along with ToStringSerializer to have the ObjectId instance represented as a string:
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
Then, in your test, use the ObjectId#toString() method (or ObjectId#toHexString()) for the assertion:
.andExpect(jsonPath("$.id", is(id.toString())))
Alternatively, assuming that you are using Spring Data for MongoDB, instead of ObjectId, you could use:
#Id
private String id;
You also could handle the conversion of ObjectId to String in your mapper layer.

Registring CustomPropertyEditors for using in BeanWrapper

I am trying to register custom property editors using the following configuration in my spring boot application.Reffered the following documentation link section 5.4.2.1.
#Bean
public static CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
configurer.setPropertyEditorRegistrars(new PropertyEditorRegistrar[] {
(registry) -> registry.registerCustomEditor(Instant.class, new CustomInstantEditor()) });
return configurer;
}
When I created a BeanWrapper and using it I am getting the following error
Code:
BeanWrapper newAccountWrapper = new BeanWrapperImpl(newAccount);
newAccountWrapper.setPropertyValue("chardate", value);
Error is:
Exception is Failed to convert property value of type [java.lang.String] to required type [java.time.Instant] for property 'chardate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.time.Instant] for property 'chardate': no matching editors or conversion strategy found
But the above code works if I register the CustomEditor for the BeanWrapper
BeanWrapper newAccountWrapper = new BeanWrapperImpl(newAccount);
newAccountWrapper.registerCustomEditor(Instant.class, new
CustomInstantEditor());
So can I not register customPropertyEditors using CustomEditorConfigurer BeanFactoryPostProcessor ?
Additional Info:
BeanWrapper newAccountWrapper = new BeanWrapperImpl(newAccount);
newAccountWrapper.registerCustomEditor(Instant.class, new CustomInstantEditor());
newAccountWrapper.registerCustomEditor(Money.class, new CustomMoneyEditor());
newAccountWrapper.setAutoGrowNestedPaths(true);
accountDomainElements.forEach((accountElement, value) -> {
newAccountWrapper.setPropertyValue(accountElement, value);
Give a try
#Bean
public CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
customEditors.put(Instant.class, CustomInstantEditor.class);
configurer.setCustomEditors(customEditors);
return configurer;
}

Resources