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);
}
}
Related
I'm trying to write Junit test cases for BigQuery using Mockito and Junit5. I'm trying to mock or even initialize Table but am not able to do so is there any way we can do it
private JsonStreamWriter streamWriter;
void WriteToBQ(TableName parentTable) {
BigQuery bigquery = BigQueryOptions
.newBuilder()
.setProjectId(AppConstants.PROJECT_ID)
.build()
.getService();
Table table = bigquery.getTable(parentTable.getDataset(), parentTable.getTable());
Schema schema = table.getDefinition().getSchema();
TableSchema tableSchema = BqToBqStorageSchemaConverter.convertTableSchema(schema);
streamWriter = JsonStreamWriter.newBuilder(parentTable.toString(), tableSchema).build();
}
I tried initialise and mock Table using
table = new Table(bigquery, new TableInfo.BuilderImpl(TABLE_INFO));
but I can't use BuilderImpl outside the package as not being public
I have even tried to Mock Table, but still no luck
bigquery = mock(BigQuery.class);
mockOptions = mock(BigQueryOptions.class);
table = mock(Table.class);
when(bigquery.getTable(any(),any())).thenReturn(table);
when(bigquery.getOptions()).thenReturn(mockOptions);
If I had to write the test for this scenario, I would go making a slight change to the class, I would extract the BigQuery object creation and move it to a protected method like the one below and override the Object creation in test implementation
public class Writer {
private JsonStreamWriter streamWriter;
void WriteToBQ(TableName parentTable) throws Descriptors.DescriptorValidationException, IOException, InterruptedException {
BigQuery bigquery = buildBigQueryService();
Table table = bigquery.getTable(parentTable.getDataset(), parentTable.getTable());
Schema schema = table.getDefinition().getSchema();
TableSchema tableSchema = BqToBqStorageSchemaConverter.convertTableSchema(schema);
streamWriter = JsonStreamWriter.newBuilder(parentTable.toString(), tableSchema).build();
}
protected BigQuery buildBigQueryService(){
return BigQueryOptions
.newBuilder()
.setProjectId("AppConstants.PROJECT_ID")
.build()
.getService();
}
}
The test should be something like
class WriterTest {
BigQuery bigquery;
Table table;
#BeforeEach
public void setup() {
bigquery = mock(BigQuery.class);
table = mock(Table.class);
when(bigquery.getTable(any(), any())).thenReturn(table);
}
#Test
public void testWriteToBQ() throws Descriptors.DescriptorValidationException, IOException, InterruptedException {
TableName tableName = TableName.newBuilder().setTable("A").build();
Writer writer = new MockWriter();
writer.WriteToBQ(tableName);
}
class MockWriter extends Writer {
#Override
protected BigQuery buildBigQueryService() {
return mock(BigQuery.class);
}
}
}
Here in the MockWriter, I have just overridden the buildBigQueryService which gives mock BigQuery.class other functionalities that remain the same as the parent class
Also, it is not recommended to mock data so please consider build real table object instead of this
table = mock(Table.class);
I am writing test case for service layer using JUnit 5 and Mockito. I am mocking database layer using #Mock and injecting into service layer using #InjectMocks. But, when call goes to service method, somehow mocked list from DAO is coming as empty. I am having similar kind of set up for other test classes and it is working fine. I even tried in same class by creating a simple flow which accepts a string argument and returning a string object and it worked. But somehow for this method, its not working for me. While debugging, I checked parameters are being passed as expected, its just DAO layer is giving empty list even after mocking it. Please let me know what wrong I am doing here.
Service Layer
#Service
public class XyzServiceImpl implements XyzService {
#Autowired
private XyzDAO xyzDAO;
#Override
public Map<String, String> getRecords(Map<String, String> allParams) throws Exception {
String key = allParams.get("key");
String configValue = System.getProperty(key);
XyzRoot xyzRoot = new ObjectMapper().readValue(configValue, XyzRoot.class);
List<Map<String, Object>> records = xyzDao.getRecords(xyzRoot, allParams); // list is coming as empty
for (Entry<String, Object> entry : records.get(0).entrySet()) {
recordsMap.put(entry.getKey(), entry.getValue()!= null ? entry.getValue().toString() : "");
}
return recordsMap;
}
}
Here is the code for test class
public class TestXyzService {
#InjectMocks
private XyzServiceImpl xyzServiceImpl;
#Mock
private xyzDAO xyzDao;
private static String data = null;
#BeforeEach
public void init() {
MockitoAnnotations.initMocks(this);
}
#BeforeAll
public static void setUp() throws IOException {
data = FileUtils.loadFileData("record-history.json");
}
#Test
void getRecordTest() throws Exception {
Gson gson = new Gson();
Map<String, String> expectedDetails = gson.fromJson(data,
new TypeToken<Map<String, String>>() {
}.getType());
Map<String, Object> recordDetailsMap = gson.fromJson(data,
new TypeToken<Map<String, Object>>() {
}.getType());
List<Map<String, Object>> recordDetails = new ArrayList<>();
recordDetails.add(recordDetailsMap);
Map<String, String> allParams = new LinkedHashMap<>();
allParams.put(AppConstants.PARAM_PAGE_NAME, "HISTORY_TBL");
allParams.put(AppConstants.PARAM_ARG1, AppConstants.ARG1);
XyzRoot xyzRoot = new XyzRoot();
xyzRoot.setTable("TEST_TBL");
Configuration configuration = new Configuration();
configuration.setArgument("COL");
xyzRoot.setConfig(configuration);
String config = gson.toJson(xyzRoot);
System.setProperty("key", config);
when(xyzDao.getRecords(xyzRoot, allParams)).thenReturn(recordDetails);
Map<String, String> actualDetails = xyzServiceImpl.getRecords(allParams); // getting error due to empty list from dao
assertNotNull(actualDetails);
assertEquals(expectedDetails, actualDetails);
verify(xyzDaoDao, times(1)).getRecords(xyzRoot, allParams);
}
}
The object created by ObjectMapper in this line:
XyzRoot xyzRoot = new ObjectMapper().readValue(configValue, XyzRoot.class);
is an instance that is completely separated from the instance you're creating in the test:
XyzRoot xyzRoot = new XyzRoot();
xyzRoot.setTable("TEST_TBL");
You do not have an equals method implemented for XyzRoot, so simple reference equality verification (==), which is a default Object implementation inherited by all classes, returns false as the objects are two completely separate instances. That's why when(...).thenReturn(...) defined in your test is not working properly - when Mockito checks if it should fire for given object, it uses equals method by default.
To solve the problem, you should do one of the following:
define equals and hashCode for XyzRoot (remember about the contract)
use argThat argumentMatcher
use refEq argument matcher
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.
Currently I'm trying to work with Mockito combined with rest. I'm not sure how to test my edit method in rest with Mockito and rest. The last block of code contains the part I can't figure out atm. I haven't worked with Mockito before so all tips are welcome.
This is the functionality i want to test:
#POST
#Path("edit/{id}")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Profile editProfile(Profile profile, #PathParam("id") Long id){
Profile updatedProfile = profileService.getProfile(id);
updatedProfile.setBiography(profile.getBiography());
updatedProfile.setLocation(profile.getLocation());
updatedProfile.setWebsite(profile.getWebsite());
updatedProfile.setAvatar(profile.getAvatar());
updatedProfile.setImage(profile.getImage());
updatedProfile.setUpdated_at(new Date());
return updatedProfile;
}
Setting up my TestClass here
Client client;
WebTarget root;
static final String PATH = "/MyApp/api/profile/";
static final String BASEURL = "http://localhost:8080" + PATH;
List<Profile> profileList = new ArrayList<Profile>();
Profile profile;
#Mock
ProfileDao profileDao;
#InjectMocks
private ProfileService profileService;
#Before
public void setUp() {
this.client = ClientBuilder.newClient();
this.root = this.client.target(BASEURL);
profile = new Profile(1L,"biography", "location" ,"website","../avatar.jpg","../image.jpg" );
for(int i = 0; i < 10; i++){
profileList.add(profile);
}
}
The Test function to edit an test: /profile/edit/1 - /profile/edit/{id}
// This part doesn't work. I'm not sure how to handle this part with mockito and rest
#Test
public void editProfileTest() {
String mediaType = MediaType.APPLICATION_JSON;
when(profileDao.find(1L)).thenReturn(new Profile(1L,"biography", "location" ,"website", "../avatar.jpg","../image.jpg"));
final Entity<Profile> entity = Entity.entity(profileService.getProfile(1L), mediaType);
Profile profileResult = this.root.path("edit/1").request().post(entity, Profile.class);
assertThat(profileResult, is(profile)); // this doesn't match
}
At first I wasn't really sure how to make the test valid but with the following piece of code I solved my problem.
Adjusted my Rest / Mockito test a little bit:
#Test
public void editProfileTest() {
String mediaType = MediaType.APPLICATION_JSON;
when(profileDao.find(2L)).thenReturn(profileList.get(0));
final Entity<Profile> entity = Entity.entity(profileService.getProfile(2L), mediaType);
Profile profileResult = this.root.path("edit/2").request().post(entity, Profile.class);
assertThat(profileResult.getId(), is(profileService.getProfile(2L).getId()));
assertThat(profileResult.getUser_id(), is(profileService.getProfile(2L).getUser_id()));
assertThat(profileResult.getAvatar(), is(profileService.getProfile(2L).getAvatar()));
assertThat(profileResult.getBiography(), is(profileService.getProfile(2L).getBiography()));
assertThat(profileResult.getLocation(), is(profileService.getProfile(2L).getLocation()));
assertThat(profileResult.getWebsite(), is(profileService.getProfile(2L).getWebsite()));
assertThat(profileResult.getImage(), is(profileService.getProfile(2L).getImage()));
}
This is testing the method on /MyApp/profile/edit/{id} only. If you have examples for an integration test or references feel free to post about it. As I'm trying to learn some more about testing in general.
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();
...