How to mock software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable.getItem?
So far I have tried the below, which is throwing NullPointerException from inside the SDK.
Any idea how to mock the table CRUD operations?
#Mock private DynamoDbEnhancedClient enhdynamodb;
#Mock private DynamoDbClient dynamodb;
#Mock private DynamoDbTable<EventRecord> dyamodbTable;
#Mock private SecurityContext securityContext;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(securityContext.getUserPrincipal()).thenReturn(principal);
enhdynamodb = DynamoDbEnhancedClient.builder().dynamoDbClient(dynamodb).build();
dyamodbTable = enhdynamodb.table(TABLE_NAME, TableSchema.fromBean(EventRecord.class));
service = new EventsService(tokenSerializer, enhdynamodb, configProvider, clock);
service.setSecurityContext(securityContext);
}
#Test
public void getEvent_null_notFound() {
String userId = UUID.randomUUID().toString();
String eventId = UUID.randomUUID().toString();
GetItemResponse response = GetItemResponse.builder().build();
EventRecord event = null;
when(principal.getName()).thenReturn(userId);
when(dyamodbTable.getItem(any(GetItemEnhancedRequest.class))).thenReturn(event);
assertThatThrownBy(() -> service.getEvent(eventId)).isInstanceOf(NotFoundApiException.class);
}
public Event getEvent(String eventId) {
log.info("Getting event {}", eventId);
EventRecord eventRecord = loadEvent(eventId);
return modelMapper.map(eventRecord, Event.class);
}
private EventRecord loadEvent(final String eventId) {
String userId = securityContext.getUserPrincipal().getName();
EventRecord event =
getTable()
.getItem(
GetItemEnhancedRequest.builder()
.consistentRead(Boolean.TRUE)
.key(k -> k.partitionValue(userId).sortValue(eventId).build())
.build());
if (event == null) {
throw new NotFoundApiException(
new NotFoundException()
.errorCode("EventNotFound")
.message(String.format("Event %s can not be found.", eventId)));
}
return event;
}
private DynamoDbTable<EventRecord> getTable() {
return dynamodb.table(tableName, TableSchema.fromBean(EventRecord.class));
}
I tried it like this and it does not throw exceptions.
#Test
public void getEvent_null_notFound() {
String userId = UUID.randomUUID().toString();
String eventId = UUID.randomUUID().toString();
DynamoDbTable dynamoDbTable = mock(DynamoDbTable.class);
EventRecord event = null;
when(dynamoDbTable.getItem(any(GetItemEnhancedRequest.class))).thenReturn(event);
assertEquals(event, dynamoDbTable.getItem(event));
}
Note that I mocking DynamoDbTable instead of DynamoDbEnhancedClient.
Mocking calls to the client and doing unit test on your own code is of course a good idea but I highly recommend using the local dynamodb library if you want to do an actual DyanmoDb calls with a local DB.
Here is full documentation. If you use this library in your unit tests you dont need to mock the calls.
Related
I have a Java Springboot web API project that uses Azure table storage as the data store. I'd like to create a unit test to make sure that the repository is properly converting an Azure TableEntity into a custom Tag object in the repository. However, I am not able to figure-out a way to mock the Azure PagedIterable<TableEntity> that is returned by the Azure TableClient.listEntities() function.
At the core of my repository class is the following function that returns a filtered list of table entities:
private PagedIterable<TableEntity> getFilteredTableRows(String filter, String tableName) {
ListEntitiesOptions options = new ListEntitiesOptions().setFilter(filter);
TableClient tableClient = tableServiceClient.getTableClient(tableName);
PagedIterable<TableEntity> pagedIterable = tableClient.listEntities(options, null, null);
return pagedIterable;
}
How do I ensure the TableClient is mocked and returns a valid PagedIterable<TableEntity>?
Below is sample JUnit test class that uses Mockito to mock the Azure PagedIterable<T> object and return a single TableEntity that is mapped to a custom Tag model in the repository code.
The test setup requires four mocks:
A mock Iterator
A mock PagedIterable
A mock TableServiceClient
A mock TableClient
If there is an easier way to accomplish the same thing, I'm open to suggestions.
#ExtendWith(MockitoExtension.class)
#MockitoSettings(strictness = Strictness.LENIENT)
public class DocTagRepositoryTest {
#InjectMocks
#Spy
DocTagRepository docTagRepository;
#Mock
TableServiceClient tableServiceClient;
#Mock
TableClient tableClient;
private static TableEntity testTableEntity;
private static Tag testTagObject;
#SneakyThrows
#BeforeAll
public static void setup() {
loadTableObjects();
}
#Test
public void testGetTagList() {
// Given: A request to get tags from Azure table storage...
Iterator mockIterator = mock(Iterator.class);
when(mockIterator.hasNext()).thenReturn(true, false);
when(mockIterator.next()).thenReturn(testTableEntity);
PagedIterable mockPagedTableEntities = mock(PagedIterable.class);
when(mockPagedTableEntities.iterator()).thenReturn(mockIterator);
when(tableServiceClient.getTableClient(Mockito.anyString())).thenReturn(tableClient);
when(tableClient.listEntities(any(), any(), any())).thenReturn(mockPagedTableEntities);
List<Tag> expected = new ArrayList<>();
expected.add(testTagObject);
// When: A call is made to the repository's getActiveTags() function...
List<Tag> actual = docTagRepository.getActiveTags();
// Then: Return an array of tag objects.
assertArrayEquals(expected.toArray(), actual.toArray());
}
private static void loadTableObjects() {
OffsetDateTime now = OffsetDateTime.now();
String testUser = "buh0000";
String rowKey = "test";
String partitionKey = "v1";
String activeStatus = "A";
Map<String, Object> properties = new HashMap<>();
properties.put("createdDate", now);
properties.put("createdBy", testUser);
properties.put("modifiedDate", now);
properties.put("lastModifiedBy", testUser);
properties.put("status", activeStatus);
testTableEntity = new TableEntity(partitionKey, rowKey);
testTableEntity.setProperties(properties);
testTagObject = new Tag(partitionKey, rowKey, now, testUser, now, testUser, activeStatus);
}
}
Long story, but I had to redesign an application this weekend. From a spring boot app to a spring batch app. The process was always a batch process, but I tried to make this batch engine and it got way too complex and i had to stop what I was doing. I'm sure we've all been there. Anyway everything is working fine!! Except for one piece of code that I tried to keep the original piece of code for. I'm trying to use a JPARepository save method and it's not working!! I am able to call the save method, I feel like the Repo is instantiated because I'm not getting a null pointer exception. In fact, I'm not getting any exceptions thrown. I am just not seeing anything in the DB. And I know this code has worked because I had it running in the previous design. Anyway here are my classes...
Data object:
#Data
#Entity
#Table(name="PAYEE_QUAL_LS")
public class PayeeList {
#EmbeddedId
private PayeeListPK payeeListPK = new PayeeListPK();
#Column(name = "PAYEE_QUAL_CD")
private String payeeQualCode;
#Column(name = "ETL_TS")
private Timestamp etlTimestamp;
}
Primary key data class...
#Data
#Embeddable
public class PayeeListPK implements Serializable {
#Column(name = "PAYEE_NM")
private String payeeName;
#Column(name = "BAT_PROC_DT")
private Date batchProcDate;
}
Repo class...
#Repository
public interface PayeeListRepo extends JpaRepository<PayeeList,String> {}
My Service class...
public class OracleService {
private static final Logger logger = LoggerFactory.getLogger(OracleService.class);
#Autowired
PayeeListRepo payeeListRepo;
public void loadToPayeeListTable(PayeeList payeeList) {
payeeListRepo.save(payeeList);
}
I have an implementation of Tasklet which I am calling from my batch Step...
public class PayeeListTableLoad implements Tasklet {
private static final Logger logger = LoggerFactory.getLogger(PayeeListTableLoad.class);
private java.sql.Date procDt;
private String inputFile;
private Timestamp time;
private int safeRecordCount = 0;
private int blockRecordCount = 0;
private int safeRejectRecordCount = 0;
private int blockRejectRecordCount = 0;
private ArrayList<String> rejectRecordList = new ArrayList<>();
#Autowired
OracleService oracleService;
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
java.util.Date parsed = format.parse(System.getenv("procDt"));
procDt = new java.sql.Date(parsed.getTime());
inputFile = Constants.filePath;
time = new Timestamp(System.currentTimeMillis());
logger.info("Running data quality checks on input file and loading to Oracle");
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) {
String line = reader.readLine();
while (line != null) {
if (dataQuality(line)) {
PayeeList payeeList = buildPayeeListObject(line);
oracleService.loadToPayeeListTable(payeeList);
logger.info("Record loaded: " + line);
} else {
rejectRecordList.add(line);
try {
if (line.split("\\|")[1].equals("B")) {
blockRejectRecordCount++;
} else if (line.split("\\|")[1].equals("S")) {
safeRejectRecordCount++;
}
logger.info("Record rejected: " + line);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
line = reader.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
logger.info("Safe record count is: " + safeRecordCount);
logger.info("Block record count is: " + blockRecordCount);
logger.info("Rejected records are: " + rejectRecordList);
SendEmail sendEmail = new SendEmail();
sendEmail.sendEmail(Constants.aegisCheckInclearingRecipient,Constants.aegisCheckInclearingSender,Constants.payeeListFileSuccessEmailSubject,Constants.payeeListFileSuccessEmailBodyBuilder(safeRecordCount,blockRecordCount,safeRejectRecordCount,blockRejectRecordCount,rejectRecordList));
logger.info("Successfully loaded to Oracle and sent out Email to stakeholders");
return null;
}
In my batch configuration....
#Bean
public OracleService oracleService() { return new OracleService(); }
#Bean
public PayeeListTableLoad payeeListTableLoad() {
return new PayeeListTableLoad();
}
#Bean
public Step payeeListLoadStep() {
return stepBuilderFactory.get("payeeListLoadStep")
.tasklet(payeeListTableLoad())
.build();
}
#Bean
public Job loadPositivePayFile(NotificationListener listener, Step positivePayLoadStep) {
return jobBuilderFactory.get("loadPositivePayFile")
.incrementer(new RunIdIncrementer())
.listener(listener)
.start(positivePayDataQualityStep())
.next(initialCleanUpStep())
.next(positivePayLoadStep)
.next(metadataTableLoadStep())
.next(cleanUpGOSStep())
.build();
}
Ultimately our step is running an implementation of Tasklet, we are Autowiring out OracleService class, and then that is being called and is then calling the Repo method. I am getting to the Oracle Service class method and I am calling the save method of my Autowired Repository but again nothing is happening!!
EDIT!!!
I have figured out another way to do it and that is with EntityManager and using the persist and flush methods. Below is now my loadToPayeeListTable method in my Oracle Service class...
public void loadToPayeeListTable(PayeeList payeeList) throws ParseException {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(payeeList);
entityManager.flush();
transaction.commit();
entityManager.close();
}
Could you have a try to passe the repository with a Spring Test? I have never met this problem, but I am not sure about the DB type. Is it Mysql, Oracle? Because I never used it with #EmbeddedId.
IF you passed the unit test, you ought to check your service logic with debugging. Opposite, you ought to passe the test first.
Change your jpa repository to
#Repository
public interface PayeeListRepo extends JpaRepository<PayeeList, PayeeListPK>
I have a Spring-Boot project. As mentioned above, I want to use AbstractRoutingDataSource to switch DataSouce. I don't want to use #Transactional since no transactions are needed.
But when I transfer my method, it seems like which the #Transactional is being used, but when I test my method in unit test, it runs ok, I think it's strange because in unit-test, #Transactional shouldn't work.
public class DynamicDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DynamicContextHolder.peek();
}
}
The controller method
#RequestMapping(value = "/pipeline/analyze/{systemId}/{dbName}/{dbSchemaName}/{dbTableName}", method = RequestMethod.GET)
public String pipelineAnalyze(#PathVariable("systemId") Long systemId, #PathVariable("dbName") String dbName, #PathVariable("dbSchemaName") String dbSchemaName, #PathVariable("dbTableName") String dbTableName) throws IOException {
ETLServerAccessEntityInterface etlServerAccessEntity = etlServer.getETLServerAccessEntity();
/* LOGGER.info("===etlServerAccessEntity.getAccessToken()===");
LOGGER.info(etlServerAccessEntity.getAccessToken());
LOGGER.info("isAlive=" + etlServer.isAlive());*/
TableInfoMasterEntity tableInfoMaster = new TableInfoMasterEntity();
TableInfoMasterEntityPk tableInfoMasterPk = new TableInfoMasterEntityPk();
tableInfoMasterPk.setSystemId(systemId);
tableInfoMasterPk.setDbName(dbName);
tableInfoMasterPk.setDbSchemaName(dbSchemaName);
tableInfoMasterPk.setDbTableName(dbTableName);
tableInfoMaster.setId(tableInfoMasterPk);
Example<TableInfoMasterEntity> exampleTableInfoMaster = Example.of(tableInfoMaster);
List<TableInfoMasterEntity> filteredTableInfoMaster = tableInfoMasterService.findAll(exampleTableInfoMaster);
System.out.println("filteredTableInfoMaster.size()=" + filteredTableInfoMaster.size());
TableInfoMasterEntity tableInfoMasterEntity = filteredTableInfoMaster.get(0);
pipelineRequirementAnalyzeService.analyze(tableInfoMasterEntity);
return String.valueOf(pipelineRequirementAnalyzeService.toString());
}
the unit test
#Test
public void findByTemplateNameContaining() throws IOException {
ETLServerAccessEntityInterface etlServerAccessEntity = etlServer.getETLServerAccessEntity();
TableInfoMasterEntity tableInfoMaster = new TableInfoMasterEntity();
TableInfoMasterEntityPk tableInfoMasterPk = new TableInfoMasterEntityPk();
tableInfoMasterPk.setSystemId(1);
tableInfoMasterPk.setDbName("DGRAMES");
tableInfoMasterPk.setDbSchemaName("MESSERIES");
tableInfoMasterPk.setDbTableName("TBLEMSACCESSORYSTATE_MAINTAIN");
tableInfoMaster.setId(tableInfoMasterPk);
Example<TableInfoMasterEntity> exampleTableInfoMaster = Example.of(tableInfoMaster);
List<TableInfoMasterEntity> filteredTableInfoMaster = tableInfoMasterService.findAll(exampleTableInfoMaster);
TableInfoMasterEntity tableInfoMasterEntity = filteredTableInfoMaster.get(0);
pipelineRequirementAnalyzeService.analyze(tableInfoMasterEntity);
System.out.println();
}
These two method are similar, they all transfers the pipelineRequirementAnalyzeService.analyze(tableInfoMasterEntity);
but the results are different. here is the failure result and the success result.
I have some handler(in this example I just use controller):
#RestController
public class MessageController {
private final TaskExecutor taskExecutor;
private final Operation operation;
private final MessageLogService messageLogService;
public MessageController(TaskExecutor taskExecutor, Operation operation, MessageLogService messageLogService) {
this.taskExecutor = taskExecutor;
this.operation = operation;
this.messageLogService = messageLogService;
}
#PostMapping(value = "/process")
public String handleMessage(MessageRequest messageRequest){
MessageLog messageLog = messageLogService.createNewMessageLog();
taskExecutor.execute(() -> {
try {
operation.process(messageLog.getGuid(), messageRequest);
} catch (MessageLogDoesNotExistException e) {
throw new RuntimeException(e);
}
});
return "REQUEST_QUOTED";
}
}
I receive some request.
I create new MessageLog in DB with status "NEW" and some default(and some data from the request in the real project) values and save.
I send messageRequest and MessageLog's guid to the operation in the executor and return sync response "REQUEST_QUOTED" immediately.
#Service
public class MessageOperation implements Operation {
private final MessageLogService messageLogService;
public MessageOperation(MessageLogService messageLogService) {
this.messageLogService = messageLogService;
}
#Transactional
#Override
public void process(String guid, MessageRequest messageRequest) throws MessageLogDoesNotExistException {
MessageLog messageLog = messageLogService.getOne(guid);
if (messageLog == null)
throw new MessageLogDoesNotExistException();
try {
Message message = createMessage(messageRequest);
messageLog.setStatus("SUCCESS");
messageLog.setMessage(message);
} catch (MessageCreationException e) {
messageLog.setStatus("FAIL");
messageLog.setErrorCode(e.getCode());
}
messageLogService.save(messageLog);
}
private Message createMessage(MessageRequest messageRequest) throws MessageCreationException {
//logic
return null;
}
}
Into operation I create the message and bind it with messageLog. If I create and bind success - I set status 'SUCCESS' or 'FAIL' if not. And just save messageLog.
How can I create Unit test for operation's method process? It is void.
1) I get a request from the client
2) delegate the request to the new thread for the async process
3) return sync response.
And I don't understand how can I create a unit test for public void process(String guid, MessageRequest messageRequest)
In this case for MessageOperation I recommend using Mockito https://www.baeldung.com/mockito-annotations library for mocking the class attribute
#Mock
private final MessageLogService messageLogService;
Then in your unit test you use the verify()method to check that expected behaviour has happened. (save is called correctly for example).
I would also mock the response of getOne to fit your need
MessageLog messageLog = messageLogService.getOne(guid);
for example
MessageLog messageLog = new MessageLog();
when(messageLogService.getOne(eq("THE GUID YOU GIVE IN THE METHDO CALL"))).thenReturn(messageLog);
That way since you have the object reference to MessageLogyou can check for the status in the test code:
assertEquals("SUCCESS", messageLog.getStatus());
And use verify to check that the save method is called correctly:
verify(messageLogService).save(same(messageLog));
About the matchers I used https://www.baeldung.com/mockito-argument-matchers
I'm trying to test a method intialize transaction where the method creates a unique transaction Id. This method uses reference of other class to retrieve the properties.
After mocking the reference classes am still getting null pointer exception. when i try to test. Below is my code.
Note: JMockito
Any help appreciated
public ResponseDto initializeTransaction(RequestDTO request){
try {
String transactionId =getTransactionId(request);
ResponseDTO result = new ResponseDTO();
result.setTransactionId(transactionId);
return result;
}
}
public String getTransactionId(CreditCardGwtInitializeRequestDTO request){
StringBuffer transactionId = new StringBuffer();
String customerId = customerIdentifier.getCustomer();
UserDto userDto = user.getUserDetails(request.getKey());
String userWorkStationId =userDto.getWorkStationId();
transactionId.append(String.valueOf(System.currentTimeMillis()) + "-");
transactionId.append(userDto.getObjId()+ "-");
transactionId.append(transactionIdEncode.encode(customerId));
transactionId.append("-");
transactionId.append(transactionIdEncode.encode(userWorkStationId));
return transactionId.toString();
}
Test class
public class CreditCardGwtInitializeServiceImplTest {
private CreditCardGwtInitializeServiceImpl test;
#Mock
private CustomerIdentifier customerIdentifier;
#Mock
private UserDto userDto;
#Mock
private UserDetails user;
private CreditCardGwtInitializeRequestDTO request;
#Mock
TransactionIdCharacterEncoding transactionId;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void when_profilename_notNull_retrieveByName() throws Exception {
//test.setUser(user);
CreditCardGwtInitializeResponseDTO expected = new CreditCardGwtInitializeResponseDTO();
expected.setGatewayPassKey("");
String profileName="theName";
String connectionKey ="123456";
String custId ="custId";
request.setGatewayProfile(profileName);
request.setConnectionKey(connectionKey);
//userDto.setWorkStationId("12345");
//userDto.setObjId(12345L);
when(customerIdentifier.getCustomer()).thenReturn(custId);
when(user.getUserDetails(anyString())).thenReturn(userDto);
when(userDto.getWorkStationId()).thenReturn("RTYTYU");
when(userDto.getObjId()).thenReturn(1232324L);
when(transactionId.encode(anyString())).thenReturn("01010101");
CreditCardGwtInitializeResponseDTO response = test.initializeTransaction(request);
assertEquals(expected,response );
verifyZeroInteractions(gatewayProfileRetrievalService);
}
By adding thes lines solved my problem
MockitoAnnotations.initMocks(this);
test = new CreditCardGwtInitializeServiceImpl();
test.setUser(user);
test.setCustomerIdentifier(customerIdentifier);
test.setTransactionIdEncode(transactionId);
Here is an alternative solution that uses Mockito magic instead of manual initialization (replaces the setup method).
First override the default JUnit Runner class by the one provided by mockito (this has the same effect as MockitoAnnotations.initMocks(this)):
#RunWith(MockitoJUnitRunner.class)
public class CreditCardGwtInitializeServiceImplTest {
...
then instantiate your object right after your declare it and let mockito take care of the injection (this has the same effect as the 5 lines of your solution) :
#InjectMocks
private CreditCardGwtInitializeServiceImpl test = new CreditCardGwtInitializeServiceImpl();
...