I still new to Junit test. I have a switch-case as code below.
public void manageTrans(ISOMsgZxc isoMsgZxc) {
AbcEntry abcEntry = new AbcEntry();
abcEntry.setEntryMid(isoMsgZxc.getMid());
String mti = isoMsgZxc.getMti() + "." + isoMsgZxc.getProcessingCode().substring(0, 2);
String transType = "";
BigDecimal amt = new BigDecimal("00.000");
switch (mti) {
case "1234.14":
case "0212.02":
transType = "S";
amt = new BigDecimal(isoMsgZxc.getTransactionAmount()).negate();
break;
case "0400.20":
case "0200.22":
transType = "R";
amt = new BigDecimal(isoMsgZxc.getTransactionAmount());
break;
}
abcEntry.setEntryType(transType);
abcEntryRepository.saveAndFlush(abcEntry);
}
Here how I testing it by using #Test
#Test
public void manageTrans() throws Exception {
AbcEntry abcEntry = mock(abcEntry.class);
PowerMockito.whenNew(AbcEntry.class).withNoArguments()
.thenReturn(abcEntry);
ISOMsgZxc isoMsgZxc = new ISOMsgZxc();
isoMsgZxc.setMti("0100");
isoMsgZxc.setMid("0100");
isoMsgZxc.setProcessingCode("000012");
isoMsgZxc.setTransactionAmount("00.000");
txnService.manageTrans(isoMsgZxc);
verify(abcEntry).setEntryMid(isoMsgZxc.getMid());
String asd = "0400.20";
if(asd.equals("0400.20") || (mti.equals("0200.02")))
{
verify(abcEntry).setEntryType("R");
}
verify(abcEntryRepositoryMock).saveAndFlush(abcEntry);
}
So far the testing show pass. But are there any others method to test the switch-case ? What is the best way to test the switch case so all the possible value can be tested? Any help would be greatly appreciated.
Thanks in advance !
It seems like you're trying to test manageTrans, but in a strange fashion due to the structure of your code (mixing business and persistence logic).
You could have a generateEntry(ISOMsgZxc) which creates and returns the AbcEntry:
public AbcEntry generateEntry(ISOMsgZxc isoMsgZxc) {
AbcEntry abcEntry = new AbcEntry();
abcEntry.setEntryMid(isoMsgZxc.getMid());
String mti = isoMsgZxc.getMti() + "." + isoMsgZxc.getProcessingCode().substring(0, 2);
String transType = "";
BigDecimal amt = new BigDecimal("00.000");
switch (mti) {
case "1234.14":
case "0212.02":
transType = "S";
amt = new BigDecimal(isoMsgZxc.getTransactionAmount()).negate();
break;
case "0400.20":
case "0200.22":
transType = "R";
amt = new BigDecimal(isoMsgZxc.getTransactionAmount());
break;
}
abcEntry.setEntryType(transType);
return abcEntry;
}
This will allow you to test generateEntry to verify the entry after:
#Test
public void generateEntry() {
ISOMsgZxc isoMsgZxc = new ISOMsgZxc();
isoMsgZxc.setMti("0100");
isoMsgZxc.setMid("0100");
isoMsgZxc.setProcessingCode("000012");
isoMsgZxc.setTransactionAmount("00.000");
AbcEntry entry = txnService.generateEntry(isoMsgZxc);
//verfiy
verify(abcEntry).setEntryMid(isoMsgZxc.getMid());
Map<String, String> expectedValues = new HashMap<>();
expectedValues.put("0400.20", "R");
expectedValues.put("0200.02", "R");
//...
expectedValues.forEach((input, output) -> verify(input).setEntryType(output));
}
In your production code, simply call:
entryRepo.saveAndFlush(generateEntry())
Easier to maintain (room for validation), easier to test (concerns are separated).
Assuming you want to test the persistence part, you should create another test.
manageTrans would look like this:
public void manageTrans(ISOMsgZxc isoMsgZxc) {
AbcEntry entry = generateEntry();
entryRepo.saveAndFlush(entry);
}
And your test would simply check if the entry exists in the repo after calling manageTrans. Although chances are, saveAndFlush has already been tested, so the manageTrans really wouldn't need testing, as it's implementation consists of already-tested code, and there is no special integration required.
The test for the third test case could look like this.
#Test
public void manageTrans() throws Exception {
ISOMsgZxc isoMsgZxc = new ISOMsgZxc();
isoMsgZxc.setMti("0200");
isoMsgZxc.setMid("0100");
isoMsgZxc.setProcessingCode("220012");
isoMsgZxc.setTransactionAmount("00.000");
//mti should now be = 0200.00, the third case
txnService.manageTrans(isoMsgZxc);
assertThat(abcEntry.getEntryMid(), equalTo(isoMsgZxc.getMid()));
assertThat(abcEntry.getEntryType(), equalTo("R"));
verify(jcbEntryRepositoryMock).saveAndFlush(jcbEntry);
}
To cover the other test cases:
modify the two lines isoMsgZxc.setMti("0200"); and isoMsgZxc.setProcessingCode("220012"); such that they enter the correct case
you will need 5 tests for full coverage
Related
I want to write mockito for the following code , but not getting how to write for method inside method buildGetSubtenantsURL ,getSubtenants,getSubtenantName -
public void addNewMap(MapDTO mapDTO) {
`...............................`
String subtenantsURL = buildGetSubtenantsURL(null);
String subTenantsResponse = getSubtenants(subtenantsURL,iottenant);
JSONObject subTenant = getSubtenantName(subTenantsResponse);
checkForMultiplePagesSubtenants(subTenantsResponse, subtenantInfoMap,iottenant);
if(subtenantInfoMap.get(mapDTO.getSubtenantName()) != null) {
mapEntity = Maps.builder().subtenant(subtenantInfoMap.get(mapDTO.getSubtenantName()).toString()).build();
}
else {
throw new DataNotFoundException(SUBTENANT_DOESNT_EXIST);
}
String SubtenantId = subtenantInfoMap.get(mapDTO.getSubtenantName());
UriComponents assetsURL = buildGetAssetsURL(iottenant,SubtenantId);
String assetsResponse = getAssets(assetsURL, iottenant);
String mindsphereAssetId = getAssetId(assetsResponse);
}
String url = new StringBuilder().append(mindsphereBaseURL).append(mindsphereAssetsURL).toString();
UriComponents baseUriComponents = UriComponentsBuilder.fromHttpUrl(url).build();
JSONObject typeId = new JSONObject();
typeId.put(Constants.TYPEID, iottenant + "." + assetType);
typeId.put(Constants.SUBTENANT,SubtenantId);
baseUriComponents = UriComponentsBuilder.fromUri(baseUriComponents.toUri())
.queryParam(Constants.FILTER, typeId.toString()).queryParam(Constants.BASIC_FIELDS_ONLY,"true").build().encode();
return baseUriComponents;
You can't achieve this with Mockito. For that, you would need Powermock (check https://www.baeldung.com/powermock-private-method for details and examples).
Having said that, mocking private methods is a really bad practice. By doing so what will you be testing? Will this be a partial unit test (since you are not even testing the whole unit, which is the class under test)? My suggestion is that you avoid this as harder as you can.
I have built a Rally dependency, which auto creates test case, folder in Test Plan. While creating test case it checks first if there any any existing test case with same name, else it creates new test case.
This was working while total test case size was small, while the test case size increased, i am seeing duplicate test cases are created. So I made thread to wait for few seconds (Thread.sleep(8000)) after checking existing scenarios and then creating new scenario. It works by this way.
Is there better way to handle & implement this to handle any size of test case. Please advice.
String tcName = rallyMethods.getTestScenarios(parentFolder, scenarioName);
Thread.sleep(8000);
if (tcName == null) {
rallyMethods.createTestCase(parentFolder, scenarioName);
Thread.sleep(8000);
} else {
rallyMethods.updateTestCase(parentFolder, scenarioName);
Thread.sleep(8000);
}
public String getTestScenarios(String parentFolderName, String ScenarioName) throws Throwable {
String sName = null;
String pFolder;
QueryRequest testCaseRequest = new QueryRequest("TestCase");
testCaseRequest.setLimit(Integer.MAX_VALUE);
testCaseRequest.setPageSize(Integer.MAX_VALUE);
testCaseRequest.setFetch(new Fetch("FormattedID", "Name", "Workspace", "Project", "TestFolder"));
testCaseRequest.setQueryFilter(new QueryFilter("Name", "=", ScenarioName));
testCaseRequest.setWorkspace(WORKSPACE_ID);
testCaseRequest.setProject(PROJECT_ID);
QueryResponse testCaseQueryResponse = query(testCaseRequest);
int testCaseCount = testCaseQueryResponse.getTotalResultCount();
// System.out.println("TestCaseCount:" + testCaseCount);
for (int i = 0; i < testCaseCount; i++) {
JsonObject scenarioObj = testCaseQueryResponse.getResults().get(i).getAsJsonObject();
String scenarioName = String.valueOf(scenarioObj.get("Name").getAsString());
JsonElement pFolderObj = scenarioObj.get("TestFolder");
if (!(pFolderObj.isJsonNull())) {
JsonObject tFolderObj = scenarioObj.get("TestFolder").getAsJsonObject();
pFolder = String.valueOf(tFolderObj.get("Name").getAsString());
if (parentFolderName.equalsIgnoreCase(pFolder)) {
sName = scenarioName;
logger.info("Test Scenarios identified in Rally: " + sName);
} else {
logger.info("Scenario, " + ScenarioName + " not found, New Scenario will be created in Rally");
}
}
}
return sName;
}
public void createTestCase(String parentFolderName, String testCaseName) throws Throwable {
String tcName = null;
String userID = readUser();
// Query Child Folders:
QueryRequest testFolderRequest = new QueryRequest("TestFolder");
testFolderRequest.setFetch(new Fetch("Name", "Workspace", "Project"));
testFolderRequest.setQueryFilter(new QueryFilter("Name", "=", parentFolderName));
testFolderRequest.setWorkspace(WORKSPACE_ID);
testFolderRequest.setProject(PROJECT_ID);
QueryResponse testFolderQueryResponse = query(testFolderRequest);
int folderCount = testFolderQueryResponse.getTotalResultCount();
for (int i = 0; i < folderCount; i++) {
String testFolderRef = testFolderQueryResponse.getResults().get(i).getAsJsonObject().get("_ref").getAsString();
JsonObject testFolderObj = testFolderQueryResponse.getResults().get(i).getAsJsonObject();
String pFolder = String.valueOf(testFolderObj.get("Name").getAsString());
if (pFolder.equalsIgnoreCase(parentFolderName)) {
//System.out.println("Creating a test case...");
JsonObject newTC = new JsonObject();
newTC.addProperty("Name", testCaseName);
newTC.addProperty("Workspace", WORKSPACE_ID);
newTC.addProperty("Project", PROJECT_ID);
newTC.addProperty("Description", "Selenium Automated TestCase");
newTC.addProperty("TestFolder", testFolderRef);
newTC.addProperty("Method", "Automated");
newTC.addProperty("Type", "Functional");
if (!(userID == null)) {
newTC.addProperty("Owner", userID);
}
CreateRequest createRequest = new CreateRequest("testcase", newTC);
CreateResponse createResponse = create(createRequest);
if (createResponse.wasSuccessful()) {
JsonObject tcObj = createResponse.getObject();
tcName = String.valueOf(tcObj.get("Name").getAsString());
logger.info("Created test scenario name is: " + tcName);
} else {
String[] createErrors;
createErrors = createResponse.getErrors();
logger.info("Error while creating test scenario below parent folder!");
for (int j = 0; j < createErrors.length; j++) {
System.out.println(createErrors[j]);
logger.info(createErrors[j]);
}
}
}
}
}
Hmmm... I'm not too familiar with the Java REST toolkit, but I can't think of a reason why a larger set of test cases in the workspace would cause the query to fail like that.
Did you try checking testCaseQueryResponse.wasSuccessful()? If it returns false, can you see what the error is? testCaseQueryResponse.getErrors()
My first thoughts are that you should provide a reasonable value for the limit and pageSize parameters, rather than passing Integer.MAX_VALUE. And second, rather than checking if the returned test cases are in the specified parent folder, you should include a query filter to filter the test cases results on TestFolder.Name = parentFolderName. Then you should only be expecting either 1 or 0 results returned (assuming that you're expecting all test cases within a test folder to have unique names).
I would like to test method which use another one? I tried do it using Mockito like below:
EDIT: Full method
public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
switch (typeOfInformation) {
case METAR:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
break;
case TAF:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
break;
case CITY_PAIR_METAR:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS
+ pc.getDepartureAndArrivalTime().get("departureTime") //get departure time from hashmap
+ StaticValues.END_TIME_STRING
+ pc.getDepartureAndArrivalTime().get("arrivalTime")
+ StaticValues.STATION_STRING
+ pc.getOriginIcao()
+ ","
+ pc.getDestinationIcao()
+ StaticValues.MOST_RECENT_FOR_TYPED_STATIONS;
System.out.println(urlAddress);
break;
case CITY_PAIR_TAFS:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS
+ pc.getDepartureAndArrivalTime().get("departureTime")
+ StaticValues.END_TIME_STRING
+ pc.getDepartureAndArrivalTime().get("arrivalTime")
+ StaticValues.STATION_STRING
+ pc.getOriginIcao()
+ ",%20"
+ pc.getDestinationIcao()
+ StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS;
System.out.println(urlAddress);
break;
default:
System.out.println("Wrong Type of informations");
}
return urlAddress;
}
Tests:
#Test
public void forGivenTypeOfInformationAndIcaoReturnUrl() {
HashMap<String,Long> departureAndArrivalTimeTest = new HashMap<>();
departureAndArrivalTimeTest.put("departureTime", 1499264449L);
departureAndArrivalTimeTest.put("arrivalTime", 1499282449L);
PageControllerForNearestCity pcSpy = Mockito.spy(pc);
Mockito.when(pcSpy.getDepartureAndArrivalTime()).thenReturn(departureAndArrivalTimeTest);
Mockito.when(pcSpy.getOriginIcao()).thenReturn("EPWA");
Mockito.when(pcSpy.getDestinationIcao()).thenReturn("EDDF");
assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_METAR, "EPGD")); }
How can I use my mocks in that case? Is it good approach or I have to do it in other way? I would like to add that I won't add these variables as arguments for this method.
PS I thought that the method has only one resposibility, just create a string, am I wrong? Should it be divided into another one like a "Service"?
Thank you for support
Your test enters too much in implementation details.
You mock the own processings/logic of your method. So it makes the test brittle and we can wonder what you assert really.
Besides, the test is complicated to read and to maintain.
At last, the processing associated to each case matters. It is the main logic of your method :
case CITY_PAIR_METAR:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS
+ pc.getDepartureAndArrivalTime().get("departureTime") //get departure time from hashmap
+ StaticValues.END_TIME_STRING
+ pc.getDepartureAndArrivalTime().get("arrivalTime") //get arrival time from hashmap
+ StaticValues.STATION_STRING
+ pc.getOriginIcao()
+ ","
+ pc.getDestinationIcao()
+ StaticValues.MOST_RECENT_FOR_TYPED_STATIONS;
System.out.println(urlAddress);
It should be tested without mocking as you actually doing.
To do it, you should separate responsabilities by introducing a new class.
The actual class should only have a controller/dispatcher role and the new class should perform the logic with a public method by case.
In this way, you class under test could have a dependency on this class and you could mock them in a straight way.
Your actual method could finally look like :
private AddressService addressService;
public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
switch (typeOfInformation) {
(...)
case CITY_PAIR_METAR:
urlAddress = addressService.createUrl();
break;
(...)
default:
System.out.println("Wrong Type of informations");
}
return urlAddress;
}
#rafaelim After your response I updated my test class and injected mock to the class like below:
#Before
public void setUp() throws Exception {
departureAndArrivalTimeTest = new HashMap<>();
xmlParser = new XmlParser();
pc = new PageControllerForNearestCity();
departureAndArrivalTimeTest.put("departureTime", 1499264449L); //second arg dep time in sec
departureAndArrivalTimeTest.put("arrivalTime", 1499282449L); //second arg arr time in sec
}
#Test
public void forGivenTypeOfInformationAndIcaoReturnUrl() {
PageControllerForNearestCity pcSpy = Mockito.spy(pc);
xmlParser.setPc(pcSpy);
Mockito.when(pcSpy.getDepartureAndArrivalTime()).thenReturn(departureAndArrivalTimeTest);
Mockito.when(pcSpy.getOriginIcao()).thenReturn("EPWA");
Mockito.when(pcSpy.getDestinationIcao()).thenReturn("EDDF");
assertThat(StaticValuesForTest.URL_ADDRESS_FOR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.METAR, "EPGD"));
assertThat(StaticValuesForTest.URL_ADDRESS_FOR_TAF).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.TAF, "EPGD"));
assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_METAR, "EPGD"));
assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_TAF).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_TAFS, "EPGD"));
}
Test passed, but its a little bit unreadable, I have to work with "clean code" I think.
EDIT:
#davidxxx please look at this:
public class UrlAddressService {
PageControllerForNearestCity pc = new PageControllerForNearestCity();
public String createUrlForMetar() {
String urlAddressForMetars = new StringBuilder()
.append(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS)
.append(pc.getDepartureAndArrivalTime().get("departureTime"))
.append(StaticValues.END_TIME_STRING)
.append(pc.getDepartureAndArrivalTime().get("arrivalTime"))
.append(StaticValues.STATION_STRING)
.append(pc.getOriginIcao())
.append(",")
.append(pc.getDestinationIcao())
.append(StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_METARS)
.toString();
return urlAddressForMetars;
}
public String createUrlForTaf() {
String urlAddressForTafs = new StringBuilder()
.append(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS)
.append(pc.getDepartureAndArrivalTime().get("departureTime"))
.append(StaticValues.END_TIME_STRING)
.append(pc.getDepartureAndArrivalTime().get("arrivalTime"))
.append(StaticValues.STATION_STRING)
.append(pc.getOriginIcao())
.append(",%20")
.append(pc.getDestinationIcao())
.append(StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS)
.toString();
return urlAddressForTafs;
}
}
And createUrlAddress method:
public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
switch (typeOfInformation) {
case METAR:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
break;
case TAF:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
break;
case CITY_PAIR_METAR:
urlAddress = addressService.createUrlForMetar();
break;
case CITY_PAIR_TAFS:
urlAddress = addressService.createUrlForTaf();
break;
default:
System.out.println("Wrong Type of informations");
}
return urlAddress;
}
Do you think that it is better approach? I cannot reduce code during building a URL String, because there are 3 different parts of code for Tafs and Metars. Could you show me the best way how to test it if my test are bad?
I think you are in the right direction! You are mocking the dependencies of your code and that dependency is exactly the PageControllerForNearestCity!
One observation about the mock, you have to inject it on xmlParser, like this:
#Test
public void forGivenTypeOfInformationAndIcaoReturnUrl() {
// here you created the mock
PageControllerForNearestCity pcSpy = Mockito.spy(pc);
// I'll assume that xmlParser is the subject of your test
// and that you're injecting the mock like code below
xmlParser.setPageController(pcSpy);
// Configure the mock and then you do the assertion
assertThat(...)
}
PS I thought that the method has only one resposibility, just create a
string, am I wrong? Should it be divided into another one like a
"Service"?
Your method is good! It really do one thing and well and that is building an url from TypeOfInformation
My suggestion is that you refactor your code, after you write your test codes and make it pass! You can remove code duplication and make it more readable!
Remeber this:
'Any fool can write code that a computer can understand. Good
programmers write code that humans can understand.'
Martin Fowler
Good coding!
Edit
Some examples of your code with some refactoring
public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
String urlAddress;
switch (typeOfInformation) {
case METAR:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
break;
case TAF:
urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
break;
case CITY_PAIR_METAR:
// We delegate the build logic to pc because
// all the information needed to build the url
// is in the PageControllerForNearestCity class
urlAddress = pc.urlAddressForCityPairMetar();
break;
case CITY_PAIR_TAFS:
// Same
urlAddress = pc.urlAddressForCityPairTafs();
break;
default:
System.out.println("Wrong Type of informations");
}
return urlAddress;
}
class PageControllerForNearestCity {
public String urlAddressForCityPairMetar() {
return urlBasedOn(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRSS, ",", StaticValues.MOST_RECENT_FOR_TYPED_STATIONS);
}
public String urlAddressForCityPairTafs() {
return urlBasedOn(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS, ",%20", StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS);
}
// This method removes the duplication that I mentioned before
private String urlBasedOn(String mainUrl, String separator, String endString) {
return mainUrl
+ this.getDepartureAndArrivalTime().get("departureTime")
+ StaticValues.END_TIME_STRING
+ this.getDepartureAndArrivalTime().get("arrivalTime")
+ StaticValues.STATION_STRING
+ this.getOriginIcao()
+ separator
+ this.getDestinationIcao()
+ endString;
}
}
Note that after this refactoring, your forGivenTypeOfInformationAndIcaoReturnUrl test method will become much simpler. But you will have to create test for urlAddressForCityPairMetar() and urlAddressForCityPairTafs().
I am getting different provider through service call.
On that base my tab section will differ; and I want to minimize this code:
if(selectedProvider.equalsIgnoreCase("youtube")){
switch (tabName.toLowerCase()) {
case "songs":
sectionTab = "video";
break;
case "artists":
sectionTab="";
break;
case "albums":
sectionTab="channel";
break;
case "playlists":
sectionTab="playlist";
break;
}}
else if(selectedProvider.equalsIgnoreCase("soundcloud")){
switch (tabName.toLowerCase()) {
case "songs":
sectionTab = "track";
break;
case "artists":
sectionTab="artist";
break;
case "albums":
sectionTab="";
break;
case "playlists":
sectionTab="playlist";
break;
}}
else {
switch (tabName.toLowerCase()) {
case "songs":
sectionTab = "track";
break;
case "artists":
sectionTab = "artist";
break;
case "albums":
sectionTab = "album";
break;
case "playlists":
sectionTab = "playlist";
break;
}
}
One possible solution here: use a map that contains a map.
Like:
Map<String, String> soundCloudMappings = new HashMap<>();
soundCloudMappings.put("songs", "track");
...
Map<String, Map<String, String> providerMappings = ...
providerMappings.put("soundcloud", soundCloudMappings);
And then you can check if provider.toLowerCase() exists in your outer map; and then ask the inner map for the correct sectionTab entry.
But of course, this is a pretty "low level" solution. Depending on your context, you might rather look into turning these raw strings into Enums constants; and add nice mapping methods to that Enum. In other words: consider balancing flexibility (doing everything with strings) with increased compile-time safety.
I prefer to build structures to define the logic rather than coding it in situations like this.
// What tabs we have.
enum Tabs {
songs,
artists,
albums,
playlists;
// Build a lookup.
static Map<String, Tabs> lookup = Arrays.stream(Tabs.values()).collect(Collectors.toMap(e -> e.name(), e -> e));
static Tabs lookup(String s) {
return lookup.get(s);
}
}
// The providers.
enum Providers {
youtube("video","","channel","playlist"),
soundcloud("track","artist","","playlist"),
others("track","artist","album","playlist");
// Build a String lookup.
static Map<String, Providers> lookup = Arrays.stream(Providers.values()).collect(Collectors.toMap(e -> e.name(), e -> e));
Map<Tabs,String> tabs = new HashMap<>();
Providers(String track, String artist, String album, String playlists) {
tabs.put(Tabs.songs, track);
tabs.put(Tabs.artists, artist);
tabs.put(Tabs.albums, album);
tabs.put(Tabs.playlists, playlists);
}
static Providers lookup(String s) {
Providers p = lookup.get(s);
// Default to others.
return p == null ? others : p;
}
public static String getSectionTabName(String provider, String tabName) {
// Lookup the provider.
Providers p = lookup(provider);
Tabs t = Tabs.lookup(tabName);
return p.tabs.get(t);
}
}
public void test() {
String provider = "youtube";
String tabName = "albums";
String section = Providers.getSectionTabName(provider, tabName);
System.out.println(provider+"!"+tabName+" = "+section);
}
Benefits to doing it this way:
Adding new providers just requires adding new enums.
Adding new tabs is a little less trivial (add a new parameter to the Providers constructor) but still does not change the code significantly.
You can use a couple of Maps like this to do the translation
static final Map<String, String> SECTION_TAB2 = new LinkedHashMap<>();
static final Map<String, String> SECTION_TAB1 = new LinkedHashMap<>();
static {
// are there special two word outcomes
SECTION_TAB2.put("youtube songs", "artists");
SECTION_TAB2.put("youtube artists", "");
SECTION_TAB2.put("youtube albums", "channel");
SECTION_TAB2.put("soundcloud albums", "");
// if not, what are the default tab name outcomes.
SECTION_TAB1.put("songs", "track");
SECTION_TAB1.put("artists", "artist");
SECTION_TAB1.put("albums", "album");
SECTION_TAB1.put("playlists", "playlist");
}
public static String sectionTab(String selectedProvider, String tabName) {
return SECTION_TAB2.getOrDefault((selectedProvider + " " + tabName).toLowerCase(),
SECTION_TAB1.get(tabName.toLowerCase()));
}
i am unable to comment so i am commenting as answer for
public static void main(String[] args) throws IOException
{
Table<String,String,String> table= HashBasedTable.create();
table.put("youtube","songs","video");
table.put("youtube","artists","");
table.put("youtube","albums","channel");
table.put("youtube","playlists","playlist");
table.put("soundcloud","songs","track");
table.put("soundcloud","artists","artist");
table.put("soundcloud","albums","");
table.put("soundcloud","playlists","playlist");
table.put("default","songs","track");
table.put("default","artists","artist");
table.put("default","albums","albums");
table.put("default","playlists","playlist");
String sectionTab = table.get("soundcloud","artists"); // u will get artist
}
we can use guava table to avoid map of map and easy to maintain and redable
https://www.tutorialspoint.com/guava/guava_table.htm
I have the following function :
protected final ResponseBuilderImpl RESPONSE_BUILDER_IMPL = new ResponseBuilderImpl(); // This is taken from jersey-core-1.11.jar
protected Response buildAcceptedResponse(Object entity) {
return buildResponse(entity, Response.Status.ACCEPTED);
}
protected Response buildResponse(Object entity, Response.Status status) {
return RESPONSE_BUILDER_IMPL.status(status).entity(entity).build();
}
I've tested several cases and noticed that only String is not working, and can't figure out why.
I'm using postman, sending some GET requests
switch (whatToTest){
case 1: //working
log.info("testing case of Interger!! ");
int x = 11;
return buildAcceptedResponse(x);
case 2: // getting as a response Unexpected 'e'
String t = "testing string";
log.info("testing case of String !!");
return buildAcceptedResponse(t);
case 3: //working
log.info("testing case of Boolean !! ");
boolean y = true;
return buildAcceptedResponse(y);
case 4: //not working
log.info("testing case of Object !! ");
Object yy = "test object";
return buildAcceptedResponse(yy);
case 5: //working
log.info("testing case of Object from type int !! ");
Object yyy = 1565746;
return buildAcceptedResponse(yyy);
case 6: //working
log.info("testing case of class with strings and ints!! ");
TestClass e = new TestClass(4,new Integer(7),"test1", false);
return buildAcceptedResponse(e);
/// Adding - when using this case - it works fine
case 7:
log.info("mapping to object");
ObjectMapper om = new ObjectMapper();
Object res = null;
String te = "test test";
try {
res = om.writeValueAsString(te);
} catch (IOException ex) {
ex.printStackTrace();
}
return buildAcceptedResponse(res);
I thought that I might have a problem with primitive types, but int and boolean are returning as they should. Also, a class containing a String inside - is also good.Can someone advise why only Strings are not working ?
Adding -
My function uses :
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("test/test")
public Response getTest(String someParam);
// Response is sr311-api\0.11\jsr311-api-0.11-sources.jar