Junit Code Coverage for Custom Row Mapper - java

I have written a Test class for getEmployeeDetails Method contains a procedure which will return employee details in a cursor, a custom row mapper implemented to map those fields.
I am able to mock the StoredProcedure call in the method and execute the method, But when i look at the code coverage Custom Row Mapper is not included as part of the Code Coverage
Below is My Repository Class
public class EmployeeRepository {
#Autowired
private JdbcRepository jdbcRepository
public List<Employee> getEmployeeDetails() {
SqlParameter[] sqlParameters = new SqlParameter[] {
new SqlOutParameter("employeedetails", OracleTypes.CURSOR, new EmployeeListHandler()) };
Map<String, Object> result = jdbcRepository.executeProcedure("employee_list_proc", sqlParameters, new HashMap<>());
return (List<Employee>) result.get("employeedetails");
}
private class EmployeeListHandler implements RowMapper<Employee> {
#Override
public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getLong("id");
employee.setName(rs.getString("name"));
// Rest of the employee Attributes
return employee;
}
}
}
Below Is My Custom JbdcRepository Class
public Class JdbcRepository {
public Map<String, Object> executeProcedure(String procedureName, SqlParameter[] sqlParameters,
Map<String, Object> inputParams){
StoredProcedure procedure = new GenericStoredProcedure();
setDataSource(procedure);
procedure.setSql(procedureName);
procedure.setFunction(false);
procedure.setParameters(sqlParameters);
return procedure.execute(inputParams);
}
}
Below Is my Test Class
#Mock
private JdbcRepository jdbcRepository;
#Mock
private ResultSet rs;
#InjectMocks
private EmployeeRepository employeeRepository;
#Test
public void testEmployeeDetails() throws Exception{
when(rs.next()).thenReturn(true).thenReturn(false);
when(rs.getString(anyString())).thenReturn("TEST");
when(jdbcRepository.executeProcedure(anyString(), any(SqlParameter[].class),
anyMap())).thenReturn(new HashMap<>());
employeeRepository.getEmployeeDetails();
verify(jdbcRepository, times(1)).executeProcedure(anyString(), any(SqlParameter[].class),
anyMap());
verifyNoMoreInteractions(jdbcRepository);
}
I have mocked the resultset, But still Code coverage is not included for the EmployeeListHandler, Not sure where i am doing wrong ..
Edit 1: If i am doing wrong, what is alternative way to get the complete code coverage .

Notice that here:
public List<Employee> getEmployeeDetails() {
SqlParameter[] sqlParameters = new SqlParameter[] {
new SqlOutParameter("employeedetails", OracleTypes.CURSOR, new EmployeeListHandler()) };
Map<String, Object> result = jdbcRepository.executeProcedure("employee_list_proc", sqlParameters, new HashMap<>());
return (List<Employee>) result.get("employeedetails");
}
You created sqlParameters and added EmployeeListHandler object as one if it's parameters.
Then You used sqlParameters with executeProcedure so your overridden function mapRow is supposed to be called when executeProcedure is called, but you also
mocked it's call
when(jdbcRepository.executeProcedure(anyString(), any(SqlParameter[].class),
anyMap())).thenReturn(new HashMap<>());
so there's no coverage on this function as it's never being called when you test it.
Answer for edit:
You should test MapRow separately.
And also, I'm not sure what you're trying to test here since almost all your function calls are mocked so this test doesn't really test anything.
If you want to actually test a real call to the repository you'll need to setup one and test it.

Related

DAO object not getting mocked in Service Layer using Junit 5 and Mockito

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 Azure PagedIterable<T>

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);
}
}

How to workaround orElseThrow mock

I have a ProductService that through ProductRepository queries a database. In there I have an update method and a find method. The update method is updateProductInDatabase(String id, Product updateInfo). The updateProductInDatabase calls a method findProductInDatabaseById(String id) which return Product or throws a ResourceNotFoundException.
My code:
public void updateProductInDatabase(String id, Product updateInfo) {
Product product = findProductInDatabaseById(id);
if (correctFormat(updateInfo.getVersion()) {
product.setVersion(updateInfo.getVersion());
repository.save(updateInfo);
//restOfTheCode
} else {
// Throws invalid input exception
}
}
private Products findProductInDatabaseById(String id) {
Optional<Product> productOptional =
repository.getAllProducts().stream.findFirst(); // return a list, but I only need the first
return productOptional.orElseThrow(...) // Throws resource not found exception
}
I want to write unit test for this code that expects the invalid input exception, but the test fails with
unexpected exception: expected InvalidInputException, but found
ResourceNotFoundException
This happens because productOptional is always an empty optional.
Can someone help in providing a workaround to mocking productOptional ?
Edit: adding my test
#Test(expected = InvalidInputException.class)
public void testUpdateProductVersionInDatabaseWhenVersionIsIncorrectFormat()
throws ApiException {
Product product = new Product();
product.setVersion("error-version");
when(repository.getAllProducts())
.thenReturn(Collections.singletonList(new Product()));
productService.updateProductInDatabase("product-id-1", product);
}
Can you do something like
List<Product> products = new LinkedList<>();
products.add(new Product()); // add product
Mockito.when(repository.getAllProducts()).thenReturn(products);
It means that the state of the DB isn't sutable for the test since you expect some data meanwhie there are no products. Insert some product before the test.
If you really don't care of the data, then yes, it could be mocked:
class ProductServiceTestClass {
#Mock
ProductRepository repository;
#InjectMocks
ProductService productService;
#Test(expected = InvalidInputException.class)
public void testUpdateProductVersionInDatabaseWhenVersionIsIncorrectFormat()
throws ApiException {
Product product = new Product();
product.setVersion("error-version");
when(repository.getAllProducts())
.thenReturn(Collections.singletonList(new Product()));
productService.updateProductInDatabase("product-id-1", product);
}
}
Note that it won't work if ProductRepository injected by #Autowired. In this case the real Spring bean will be used instead. Change its injection to constructor or setter way.
You should refactor your code so the validation method can be unit tested
Extract method correctFormat(updateInfo.getVersion() to a utility class like :
VersionValidator.isCorrectFormat(String version)
you will be able to unit test it :
#Test
public void should_return_false_on_incorrect_format_version()
throws ApiException {
String uncorrectFormat = "LTS.x.v15dssdf";
boolean isCorrect = VersionValidator.isCorrectFormat(uncorrectFormat);
assertThat(isCorrect).isFalse()
}
anyway, if you want to keep with an integration test, your method repository.getAllProducts() should at least return a product.
public void updateProductInDatabase(String id, Product updateInfo) {
if (!correctFormat(updateInfo.getVersion()) {
//throw
}
Product product = findProductInDatabaseById(id);
product.setVersion(updateInfo.getVersion());
repository.save(updateInfo);
//restOfTheCode
}

How could I create a custom dozer initializer

Is it possible to create a custom dozer initializer so it accepts a parameter with the constructor and not just an empty constructor?
For example, next converter failed because of dozer can't initialize it, and throws java.lang.InstantiationException
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
private static String myParameter;
// How could dozer accepts this constructor?
public MyCustomDozerConverter(String myParameter) {
super(MyObject.class, String.class);
this.myParameter = myParameter;
}
#Override
public String convertTo(MyObject source, String destination) {
// Using value of myParamter which passed in constructor
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
Also if it possible, so how can i send this parameter to constructor so it will be dynamic not static?
Note: i am using dozer inside spring-boot project
In your configuration class you need to add this section:
#Bean
public DozerBeanMapper mapper() throws IOException {
List<String> mappingFiles = new ArrayList<String>();
List<CustomConverter> customConverters = new ArrayList<CustomConverter>();
customConverters.add(new MyCustomDozerConverter(""));
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.setMappingFiles(mappingFiles);
mapper.setCustomConverters(customConverters);
return mapper;
}
In this way dozer will use instance of MyCustomConverter you set in mapper.setCustomConverters(customConverters) method.

Am I testing this correctly?

I have the following method on a service class:
#Service
public class Service {
(...)
public Page<ChannelAccount> getByCustomerAndChannelType(Pageable pageable, Customer customer, ChannelType channelType) {
return channelAccountRepository.findByCustomerAndChannelType(pageable, customer, channelType);
}
}
This returns the expected result. Now I trying to build the unit test for it. So far I got this:
#RunWith(MockitoJUnitRunner.class)
public class ChannelAccountServiceTest {
#InjectMocks
private ChannelAccountService channelAccountService;
#Mock
private ChannelAccountRepository channelAccountRepository;
(...)
#Test
public void testGetByCustomerAndChannelTypePageable() {
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
Mockito.when(channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK)).thenReturn(pageResult);
Page<ChannelAccount> channelAccountPage = channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK);
assertEquals(pageResult, channelAccountPage);
}
Somehow this doesn't feels right. What am I missing here?
Not sure why you are calling this method as it has nothing to do with the case itself:
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
I would do the following in the test:
Pageable pageableStub = Mockito.mock(Pageable.class);
Page pageStub = Mockito.mock(Page.class);
Mockito.when(channelAccountRepository
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK))
.thenReturn(pageStub);
Page<ChannelAccount> channelAccountPage = channelAccountService
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK);
assertTrue(pageResult == channelAccountPage);
I would check whether the objects are the same instances instead of equals (even more strict).

Categories

Resources