Please ignore the excessive proliferation of logging. I’m desperate. The code below works when I run the application as a Spring Boot Applicant and use Postman to do a PUT with all of the same ApplicantDO data. I worry that the CompletableFuture might be causing the problem, but I’m new to using it. To the best of my ability to debug so far, I keep failing in this snippet of code. I could be wrong though:
log.info("nnnApplicant converted to a Document: " + document.getString("ssn") + "nnn");
return CompletableFuture.supplyAsync(() -> {
try {
return repository.updateApplicantSync(ssn, document);
} catch (Exception e) {
log.error("Error in updateApplicantSync", e);
throw e;
}
}).thenApply(updatedDocument -> {
if (updatedDocument == null) {
log.error("No document found with SSN: " + ssn);
throw new RuntimeException("No document found with SSN: " + ssn);
}
log.info("Applicant updated in db: ");
return converter.convertDocumentToApplicantDO(updatedDocument);
}).exceptionally(e -> {
log.debug(messages.getString("error.updating.applicant") + e.getMessage());
return null;
Failure Trace:
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152)
at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
at org.junit.jupiter.api.AssertNotNull.failNull(AssertNotNull.java:49)
at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:35)
at org.junit.jupiter.api.Assertions.assertNotNull(Assertions.java:312)
at test.java.loanmarket.borrowingservice.ApplicantServiceTest.testUpdateApplicantAsync(ApplicantServiceTest.java:78)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
I have tried so many variations in the test method, but I continually get a updatedApplicant that is null. Any suggests?
class ApplicantServiceTest {
@Mock
private ApplicantRepository repository;
@Mock
private MongoDataConverter converter;
@InjectMocks
private ApplicantService service;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testUpdateApplicantAsync() throws ExecutionException, InterruptedException {
// Arrange
String ssn = "054220845";
ApplicantDO applicant = new ApplicantDO();
applicant.setSSN(ssn);
applicant.setFirstName("John");
applicant.setLastName("Doe");
applicant.setEmail("[email protected]");
applicant.setPhoneNumber("1234567890");
applicant.setDateOfBirth(LocalDate.of(1980, 1, 1));
applicant.setAddress("123 Main St");
applicant.setEmploymentStatus("Employed");
applicant.setAnnualIncome(10);
applicant.setCreditScore(700);
applicant.setCreditHistory("Good");
Document document = new Document();
document.put("ssn", ssn);
document.put("firstName", "Joe");
document.put("lastName", "Gaber");
document.put("email", "[email protected]");
document.put("phoneNumber", "1234567890");
document.put("dateOfBirth", LocalDate.of(1980, 1, 1));
document.put("addressId", "123 Main St");
document.put("employmentStatus", "Employed");
document.put("annualIncome", 100000);
document.put("creditScore", 700);
document.put("creditHistory", "Good");
when(converter.convertApplicantDOToDocument(applicant)).thenReturn(document);
when(repository.updateApplicantSync(ssn, document)).thenReturn(document);
// Act
CompletableFuture<ApplicantDO> future = service.updateApplicantAsync(ssn, applicant);
ApplicantDO updatedApplicant = future.get();
// Assert
// assertEquals(ssn, updatedApplicant.getSSN());
// Assert
assertNotNull(updatedApplicant, "updatedApplicant is null");
assertEquals(ssn, updatedApplicant.getSSN());
}
// Controller class
@PutMapping("/api/v1/loanmarket/applicant/{ssn}")
public CompletableFuture<ResponseEntity<ApplicantDO>> updateApplicant(@PathVariable String ssn,
@RequestBody ApplicantDO applicant) {
return applicantService.updateApplicantAsync(ssn, applicant).handle((result, ex) -> {
if (ex != null) {
log.debug("nn" + ex.getMessage() + (result != null ? result.getSSN() : "null") + "nn");
return handleApplicantFailure.apply(ex, result);
} else if (result == null) {
log.debug("nn Result is null nn");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} else {
log.debug("nn" + messages.getString("returning.response") + "nn");
return ResponseEntity.ok(result);
}
});
}
// Service class
public CompletableFuture<ApplicantDO> updateApplicantAsync(String ssn, ApplicantDO applicant) {
log.info(messages.getString("entering.updateApplicantAsync"));
log.info("Applicant to update: " + applicant.getSSN());
long stamp = lock.writeLock();
try {
Document document = converter.convertApplicantDOToDocument(applicant);
log.info("nnnApplicant converted to a Document: " + document.getString("ssn") + "nnn");
return CompletableFuture.supplyAsync(() -> {
try {
return repository.updateApplicantSync(ssn, document);
} catch (Exception e) {
log.error("Error in updateApplicantSync", e);
throw e;
}
}).thenApply(updatedDocument -> {
if (updatedDocument == null) {
log.error("No document found with SSN: " + ssn);
throw new RuntimeException("No document found with SSN: " + ssn);
}
log.info("Applicant updated in db: ");
return converter.convertDocumentToApplicantDO(updatedDocument);
}).exceptionally(e -> {
log.debug(messages.getString("error.updating.applicant") + e.getMessage());
return null;
});
} finally {
lock.unlockWrite(stamp);
}
}
// Repository class
public Document updateApplicantSync(String ssn, Document document) {
log.debug("nnnEntering updateApplicantn");
log.debug("nSSN: " + ssn + "n");
log.debug("nDocument: " + document + "nnn");
try {
MongoCollection<Document> collection = mongoDatabase.getCollection("applicants");
FindOneAndUpdateOptions options = new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER);
Document updatedDocument = collection
.findOneAndUpdate(Filters.eq("ssn", ssn), new Document("$set", document), options);
log.debug("Updated Document: " + updatedDocument);
return updatedDocument;
} catch (MongoException e) {
log.info("nn caught an exception: " + e.getMessage() + "nn" + e.getStackTrace().toString());
log.debug("nn" + messages.getString("repository.error.creating.object") + e.getMessage() + "nn");
throw new RuntimeException(e);
}
}
// Utility class for converting between data types
public ApplicantDO convertDocumentToApplicantDO(Document document) {
if (document == null) {
log.error(messages.getString("nnnerror.null.documentnnn"));
throw new NullPointerException(messages.getString("error.null.document"));
} else if (document.isEmpty()) {
log.error(messages.getString("nnnerror.empty.documentnnn"));
throw new NullPointerException(messages.getString("error.empty.document"));
}else {
log.debug("nnnDocument is not null or emptynnn");
}
log.debug(messages.getString("entering.convertDocumentToApplicantDO"));
ApplicantDO applicant = new ApplicantDO();
applicant.set_id(document.getObjectId("_id"));
applicant.setFirstName(Optional.ofNullable(document.getString("firstName")).orElse(""));
applicant.setLastName(Optional.ofNullable(document.getString("lastName")).orElse(""));
applicant.setEmail(Optional.ofNullable(document.getString("email")).orElse(""));
applicant.setPhoneNumber(Optional.ofNullable(document.getString("phoneNumber")).orElse(""));
applicant.setSSN(Optional.ofNullable(document.getString("ssn")).orElse(""));
applicant.setDateOfBirth(getLocalDateFromDocument(document, "dateOfBirth")); // This method already handles null
// // values
applicant.setAddress(Optional.ofNullable(document.getString("addressId")).orElse(""));
applicant.setEmploymentStatus(Optional.ofNullable(document.getString("employmentStatus")).orElse(""));
applicant.setAnnualIncome(Optional.ofNullable(document.getInteger("annualIncome")).orElse(0));
applicant.setCreditScore(Optional.ofNullable(document.getInteger("creditScore")).orElse(0));
applicant.setCreditHistory(Optional.ofNullable(document.getString("creditHistory")).orElse(""));
applicant.setLoanApplicationIds(Optional.ofNullable(document.getList("loanApplicationIds", ObjectId.class))
.orElse(new ArrayList<>()));
log.info("Successfully converted Document to ApplicantDO");
log.debug("New ApplicantDO after converting from Document" + applicant.getSSN());
return applicant;
}
I’ve tried other integration tests such as createApplicant and findApplicantBy[some field] and these tests pass.
1