I'm writing a junit test case for a method in the data access layer, how to stub/verify a complex query using mockito?
checked the following links on how to stub a complex query:
- https://howtodoinjava.com/hibernate/hibernate-criteria-queries-tutorial/
- https://github.com/MorphiaOrg/morphia/issues/933
none of them match my case, and the documentation does not say much about it
https://static.javadoc.io/org.mockito/mockito-core/2.8.9/index.html?org/mockito/Mockito.html
Actual code :
public List<Content> getContentByParams(String entity, String channelId, String sectionId,
Integer limit, String[] retrievedFields) {
Query<Content> query = this.createQuery();
if (StringUtils.isNotBlank(channelId) && StringUtils.isNotBlank(sectionId)) {
query.and(query.criteria("name").equalIgnoreCase(entity),
query.criteria("channel").equal(channelId),
query.criteria("section").equal(sectionId));
System.out.println("after===============");
}
if (retrievedFields != null && retrievedFields.length > 0) {
System.out.println("retrieved fields");
for (String field : retrievedFields) {
query.project(field, true);
}
}
if (limit == null) {
limit = 4;
}
FindOptions findOptions = new FindOptions().limit(limit);
return query.asList(findOptions);
}
Test case :
public void getContentByEntitiesAndPrimaryChannelSection() {
FieldEnd<Criteria> mockFieldEndEntity = mock(FieldEnd.class);
FieldEnd<Criteria> mockFieldEndChannel = mock(FieldEnd.class);
FieldEnd<Criteria> mockFieldEndSection = mock(FieldEnd.class);
// doReturn(mockFieldEndEntity).when(query).criteria("name");
// doReturn(mockFieldEndChannel).when(query).criteria("channel");
// doReturn(mockFieldEndSection).when(query).criteria("section");
contentDAO.getContentByParams(entity, "channel_3", "section_3", 10, mockFields);
for (String field : mockFields) {
verify(query).project(field, true);
}
ArgumentCaptor<FindOptions> argument = ArgumentCaptor.forClass(FindOptions.class);
verify(query).asList(argument.capture());
FindOptions findOptions = argument.getValue();
assertEquals(10, findOptions.getLimit());
PowerMockito.verifyStatic(MongoQueryUtil.class, times(1));
}
I am unsure on how to add a test for the query creation part with the test current status it passes but it is not testing the query creation.
any help will be appreciated or if there is a documentation for it somewhere.
Related
I am working on a code making currency conversion.
I have the following repository interface:
#Repository
public interface FxRateRepository extends JpaRepository<FxRate,Long> {
List<FxRate> findByCurrencyCode(String currencyTo, String currencyFrom);
And the following DB records:
The scenario is that there is an incoming amoint of money in EUR and i want to save it in a bank account in USD. To make so, I have to convert based on the current changing rates.
I have the follwing code snippet:
private BigDecimal getAmount(AccountData accountData, Input input) {
String currencyOfAccount = accountData.getCurrency();
String amountCurrency = input.getCurrency();
List<FxRate> res = fxRateRepository.findByCurrencyCode(currencyOfAccount, amountCurrency);
if (Objects.nonNull(res)) {
for (FxRate fxRate : res) {
if (amountCurrency.equals(currencyOfAccount)) {
return inputAmount;
} else if (currencyOfAccount.equals("HUF")) {
BigDecimal midPrice = getMidPriceBasedOnAmountCurrency(input, fxRate);
if (Objects.nonNull(midPrice)) {
return inputAmount.multiply(midPrice)
.setScale(5, BigDecimal.ROUND_UP);
}
} else if (!currencyOfAccount.equals("HUF")) {
BigDecimal midPriceBasedOnCurrencyOfAccount = getMidPriceBasedOnCurrencyOfAccount(accountData, fxRate);
if (Objects.nonNull(midPriceBasedOnAmountCurrency) && Objects.nonNull(midPriceBasedOnCurrencyOfAccount)) {
return inputAmount.multiply(midPrice).divide(midPriceBasedOnCurrencyOfAccount)
.setScale(5, BigDecimal.ROUND_UP);
}
}
}
}
return null;
}
And the following method to get the midPrice and this is the one which returns constantly null value:
private BigDecimal getMidPriceBasedOnAmountCurrency(Input input, FxRate fxRate) {
if (fxRate.getCurrencyCode().equals(input.getCurrency())) {
return fxRate.getMidPrice();
} else if (input.getCurrency().equals(fxRate.getFixingCurrencyCode())) {
return new BigDecimal("0");
} else if (!input.getCurrency().equals(fxRate.getCurrencyCode()) && !input.getCurrency().equals(fxRate.getFixingCurrencyCode())){
BigDecimal midPrice = fxRateRepository.findMidPrice(input.getCurrency());
return midPrice;
}
return null;
}
Which is based on....:
#Query("SELECT fx.midPrice FROM FxRate fx WHERE fx.currencyCode =:currencyTo")
BigDecimal findMidPrice(#Param("currencyTo") String currencyTo);
This query gives me constantly null althouh the currencyTo is replaced successfully to EUR but the DB record is not found...
I also tried with a list search:
List<FxRate> findMidPrice(String currencyTo);
... and then get the field with :
List<FxRate> midPriceList = fxRateRepository.findMidPrice(amountCurrency);
BigDecimal midPrice = midPriceList.get(0).getMidPrice();
Gives null too...
I cannot get why it does not return the correct value althouh it worked with the findByCurrencyCode like a charm....
Any idea?
UPDATE
If I put the second query to a separate new class / repository, then it is working...
I am writing unit tests for he below code. but coverage is missing for the below lines of code. I am not sure how can we cover the below lines.My research didnt help.
public DetailsResponse mapRow(ResultSet resultSet, int num) throws SQLException {
DetailsResponse DetailsResponse = new DetailsResponse();
String[] responseElements = null;
String response = resultSet.getString(1);
//coverage missing for below line
if (response != null && response.indexOf(",") != -1) {
responseElements = response.split(",");
}
//coverage missing for below line
if (responseElements != null && responseElements.length > 0) {
//coverage missing for below line
String id = StringUtils.isNotBlank(responseElements[0]) ? responseElements[0].replace("(", "") : "";
The commented lines are missing from the coverage., how can i test them?
Since this is a public method and you are trying to write a unit test, not an integration test, you can simply setup a ResultSet object. In doing so, you can set the object so that both conditions will get covered.
#Test
public void test(){
// SETUP
ResultSet resultSet = // setup ResultSet to return what looks like a comma separated list.
// TEST
DetailsResponse out = service. mapRow(resultSet, someNum);
// VERIFY / ASSERT
// some assert(s) on out
}
My feature file is as follows :-
Feature: Login to HRM with multiple roles
#SMOKE_P1
#REG_P3
Scenario: SC1_SMOKE_P1: An sysadmin should login to HRM Portal
Given Login To Application: User "sysadmin", Password: "sysadmin"
And - Click on Login button
Then - Verify Msg: "Welcome ConfigAdmin" in Header
#SMOKE_P2
#REG_P2
Scenario: SC1_SMOKE_P1: An sysadmin should login to HRM Portal
Given Login To Application: User "sysadmin", Password: "sysadmin"
And - Click on Login button
Then - Verify Msg: "Welcome ConfigAdmin" in Header
I execute as clean test -Dcucumber.options="--tags #SMOKE_P1,#SMOKE_P2"
During execution I am retrieving scenario id as :-
ScenarioID = scenario.getName().substring(0, scenario.getName().length()); // +
// "---:";;
if (ScenarioID.contains(":")) {
ScenarioID = ScenarioID.substring(0, ScenarioID.indexOf(":"));
// ScenarioID.indexOf("\"") + 1,
}
StepDefinitionLogger.info("===============================");
StepDefinitionLogger.info("My Scenario ID:" + ScenarioID);
StepDefinitionLogger.info("Scenario Tags are: "+scenario.getSourceTagNames().toString());
StepDefinitionLogger.info("===============================");
Since my Scenario has 2 tags from which only 1 is getting considered while executions. I want to retrieve the currently executing Tag.
probably a bit late but in case anyone is still wondering, here's how I achieved this:
get the current Cucumberoptions Tags passed as command line -D argument:
private String[] getTagsFromCmdParams() {
String proptag = System.getProperty("cucumber.options");
if(proptag != null && proptag.length() > 0) {
Pattern p = Pattern.compile("--tags (#[^ ]+(,#[^ ]+)*)");
Matcher m = p.matcher(proptag);
boolean b = m.matches();
if(b && m.groupCount() >= 2 ) {
String test = m.group(1);
if(test != null && test.length() > 0) {
String[] bits = test.split(",");
if(bits.length > 0) {
return bits;
}
}
}
}
return new String[]{};
}
get the current Cucumberoptions Tags from annotation (clazz being the #CucumberOptions annotated class)
private String[] getTagsFromAnnotations(Class<?> clazz) {
CucumberOptions co = clazz.getAnnotation(CucumberOptions.class);
String[] tags = co.tags();
if(tags.length == 1 && tags[0].contains(",")) {
return tags[0].split(",");
}
return new String[]{};
}
Use both in this order
public String[] getTags(Class<?> clazz) {
String[] tags = this.getTagsFromCmdParams();
if(tags.length > 0) {
return tags;
}
return getTagsFromAnnotations(clazz);
}
then using a hook, get the intersection between the scenario tags and option tags
public static String[] getStringIntersection(String[] array1, String[] array2) {
Set<String> s1 = new HashSet<>(Arrays.asList(array1));
Set<String> s2 = new HashSet<>(Arrays.asList(array2));
s1.retainAll(s2);
return s1.toArray(new String[0]);
}
#Before
public void before(final Scenario scenario) {
String[] scenarioTags = scenario.getSourceTagNames().toArray(new String[]{});
String[] optionsTags = getTags(TestRunner.class); // TestRunner has the "#CucumberOptions" annotation
String[] runningTag = getStringIntersection(scenarioTags, optionsTags);
System.out.print("scenarioTags: ");
printArray(scenarioTags);
System.out.print("optionsTags: ");
printArray(optionsTags);
System.out.print("runningTag: ");
printArray(runningTag);
}
Hope this helps,
Cheers
I am using hibernate spring where I need to generate query on a condition.
DAO.java
public ReturnData updateUserDetails(Users users, String mailID)
{
if(!users.getImageURL().equals(""))
{
Query query = this.sessionFactory.getCurrentSession().createQuery("UPDATE users SET emailID=:email_ID, name=:name, imageURL=:imageURL WHERE emailID=:emailID")
//setString....
}
else
{
Query query = this.sessionFactory.getCurrentSession().createQuery("UPDATE users SET emailID=:email_ID, name=:name WHERE emailID=:emailID")
//setString....
}
}
In the above code, I check if image also has been uploaded or not. On the basis of this condition, I have to dynamically generate query. I have to rewrite the whole code for query+execution 2 times. Is it the good way, or is there any better way to do this?
You can dynamically append the query conditions to the query string if they are not null. After getting the final list of conditions, you can create Hibernate query.
StringBuilder sqlQuery = new StringBuilder();
Map<String,Object> parameters = new HashMap<String,Object>();
boolean isFirstSearchCriterion = true;
sqlQuery.append("UPDATE users");
if(email_ID!= null && !email_ID.trim().equals("")) {
if(isFirstSearchCriterion) {
sqlQuery.append(" set emailID= :email_ID");
} else {
sqlQuery.append(" and emailID= :email_ID");
}
parameters.put("email_ID",email_ID);
isFirstSearchCriterion = false;
}
if(name!= null && !name.trim().equals("")) {
if(isFirstSearchCriterion) {
sqlQuery.append(" set name= :name");
} else {
sqlQuery.append(" and name= :name");
}
parameters.put("name",name);
isFirstSearchCriterion = false;
}
if(imageURL!= null && !imageURL.trim().equals("")) {
if(isFirstSearchCriterion) {
sqlQuery.append(" set imageURL= :imageURL");
} else {
sqlQuery.append(" and imageURL= :imageURL");
}
parameters.put("imageURL",imageURL);
isFirstSearchCriterion = false;
}
Query query = this.sessionFactory.getCurrentSession().createQuery(sqlQuery);
Set<String> parameterSet = parameters.keySet();
for (Iterator<String> it = parameterSet.iterator(); it.hasNext();) {
String parameter = it.next();
query.setParameter(parameter, parameters.get(parameter));
}
You can simply do without checking empty String, if user has image url it will add in column or else empty url will be pass on.
public ReturnData updateUserDetails(Users users, String mailID)
{
Query query = this.sessionFactory.getCurrentSession().createQuery("UPDATE users SET emailID=:email_ID, name=:name, imageURL=:imageURL WHERE emailID=:emailID")
query.setParameter("imageURL",users.getImageURL(), Hibernate.STRING);
}
I'm testing the following method:
private String getHomeStateCountry(Details aDetails, String aCountry) {
String homeState = Constants.BLANK_SPACE;
if (Constants.USA.equalsIgnoreCase((aCountry))) {
if (aDetails.getStateCountry() != null) {
homeState = aDetails.getStateCountry().getStateName();
}
} else {
if (aDetails.getStateCountry() != null) {
homeState = aDetails.getStateCountry().getCountryName();
}
}
return homeState;
}
When writing JUnits for this method, Is it pointless to set the stateName and countryName for each test(so if something goes wrong, e.g. the state is returned instead of the country, then the fail message provides a more accurate explanation etc) or should each test only set the value under test, i.e. if I'm testing for other than USA, only set countryName and check the returned value?