Verify if sorting of alphanumeric with symbol characters is correct

Please help me how to verify if sorting of alphanumeric with symbol character values in data table is correct. Sample values are: AA-1, AA-10, AA-2, AA-2 (1)

When clicking the column header to sort it as ascending, the web application displays it as:
AA-1
AA-2
AA-2 (1)
AA-10

However, when using Collections.sort(), the output in test log displays:
AA-1
AA-10
AA-2
AA-2 (1)

That’s why I am not able to verify if sorted data in the page and output of Collections.sort() are matched/equal on array list.

Please help me to figure out a simple way how to verify sorting in Katalon. Thanks!

hi,

your point is validate just the order not are lists equal?

hello,

this is one way to verify are lists equal

import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import static com.kms.katalon.core.testobject.ObjectRepository.findWindowsObject

import java.util.List

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.checkpoint.Checkpoint as Checkpoint
import com.kms.katalon.core.cucumber.keyword.CucumberBuiltinKeywords as CucumberKW
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.testcase.TestCase as TestCase
import com.kms.katalon.core.testdata.TestData as TestData
import com.kms.katalon.core.testobject.TestObject as TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.windows.keyword.WindowsBuiltinKeywords as Windows
import internal.GlobalVariable as GlobalVariable

import com.kms.katalon.core.logging.KeywordLogger

KeywordLogger logger = new KeywordLogger()

List <String> arr = new ArrayList<>()
List <String> arr2 = new ArrayList<>()
//AA-1, AA-10, AA-2, AA-2 (1)
arr.add("AA-1")
arr.add("AA-10")
arr.add("AA-2")
arr.add("AA-2 (1)")
arr.add("BB")

arr2.add("AA-10")
arr2.add("AA-1")
arr2.add("AA-2 (1)")
arr2.add("AA-2")

print arr
print arr2

Collections.sort(arr, Collections.reverseOrder());
Collections.sort(arr2, Collections.reverseOrder());

// Here aList is an ArrayList of ArrayLists
ArrayList<ArrayList<String> > aList =  new ArrayList<ArrayList<String> >(n);

aList = getNameListsCompared(arr, arr2)


if (aList.get(0).size > 0)
{
	logger.logFailed("Missing elements from list two: ")
	print ("Missing elements from list two: ")
	for (int a = 0; a < aList.get(0).size(); a++){
		print "missing name: "+aList.get(0).get(a)
		logger.logFailed("missing name: "+aList.get(0).get(a))
	}
}
else{
	logger.logInfo("No missing elements from list two: ")
	print ("No missing elements from list two: ")
}

if (aList.get(1).size() > 0){
	logger.logFailed("Missing elements from list one: ")
	print ("Missing elements from list one: ")
	for (int b = 0; b < aList.get(1).size(); b++){
		print "missing name: "+aList.get(1).get(b)
		logger.logFailed("missing name: "+aList.get(1).get(b))
	}
}
else{
	logger.logInfo("No missing elements from list one: ")
	print ("No missing elements from list one: ")
}


public ArrayList<ArrayList<String>> getNameListsCompared(List<String> arr, List<String> arr2){
	
		int n = 0;

		if (arr.size() > arr2.size()){
			n = arr.size()
		}
		else{
			n = arr2.size()
		}

			// Here aList is an ArrayList of ArrayLists
			ArrayList<ArrayList<String> > aList =  new ArrayList<ArrayList<String> >(n);
	
			List <String> listOne = new ArrayList<>();
			List <String> listTwo = new ArrayList<>();
			listOne = arr
			listTwo = arr2
	
			//find missing elements from list two
			listOne.removeAll(listTwo);
			//println "missing elements from list two " + listOne;
			aList.add(listOne)
	
			listOne = arr
			listTwo = arr2
	
			//find missing elements from list one
			listTwo.removeAll(listOne);
			//println "missing elements from list one " + listTwo;
			aList.add(listTwo)
	
			listOne = arr
			listTwo = arr2
	
			//get common elements
			listTwo.retainAll(listOne);
			//println "common elements are: " + listTwo;
			aList.add(listTwo)
	

			listOne = arr
			listTwo = arr2
	
			HashSet<String> set = new HashSet<String>();
			for (int i = 0; i < listOne.size; i++)
			{
				for (int j = 0; j < listTwo.size; j++)
				{
					if(listOne[i].equals(listTwo[j]))
					{
						set.add(listOne[i]);
					}
				}
			}
	
			//System.out.println("Common names are: "+set);
			return aList
		}

2020-04-15 17:59:32.655 ERROR c.k.katalon.core.logging.KeywordLogger - :x: Missing elements from list two:
2020-04-15 17:59:32.665 ERROR c.k.katalon.core.logging.KeywordLogger - :x: missing name: BB

Yes, I need to validate if the page displays the data table in ascending order. So I am thinking that the logic would be:

  1. Click the column header to make it in ascending order.
  2. Get the data on the sorted column then store it in array (e.g. Arr1).
  3. Get the data from Arr1 then store it in Arr2.
  4. Sort the data in Arr2 using Collections.sort() method.
  5. Verify if the stored data in Arr1[0] is equal to Arr2[0] to check that the data on the application is sorted in ascending format.

hi,

didn’t get your point in this phase
“Get the data from Arr1 then store it in Arr2.”
why to move sorted array to arr2 and sort again in step 4?

hi,

arr.add(“AA-1”)
arr.add(“AA-10”)
arr.add(“AA-2”)
arr.add(“AA-2 (1)”)

arr2.add(“AA-10”)
arr2.add(“AA-1”)
arr2.add(“AA-2 (1)”)
arr2.add(“AA-2”)

you can get the order as reversed order (descending)
Collections.sort(arr, Collections.reverseOrder());
Collections.sort(arr2, Collections.reverseOrder());

print arr
print arr2

[AA-2 (1), AA-2, AA-10, AA-1]
[AA-2 (1), AA-2, AA-10, AA-1]

Apologies if its confusing, but the purpose of that is:
Arr1 - serves as the “actual behavior” (what is being displayed on the UI of the application under the column that is clicked on to sort in ascending order)

Arr2 - will serve as the “expected behavior” using Collections.sort()

Then, it will verify if Arr1[0] is equal to Arr2[0] – which is the step 5

This is the “Actual Result” (which is correct because the testing of the manual QA team is from smallest to highest number):
AA-1
AA-2
AA-2 (1)
AA-10

But when using the Collections.sort(), the output is different from the Actual Result, that’s why the array list that I’m setting as Expected Result is not equal from the actual. That’s the main concern for now.
AA-1
AA-10
AA-2
AA-2 (1)

hi,

this will sort arr2 as it is in arr1

//Collections.sort(arr, Collections.reverseOrder());
Collections.sort(arr2, Collections.reverseOrder());
//print arr
//print arr2

//Collections.sort(arr, Collections.reverseOrder());
Collections.sort(arr2);

print arr
print arr2

[AA-1, AA-10, AA-2, AA-2 (1)]
[AA-1, AA-10, AA-2, AA-2 (1)]

It looks to me as though the application is using a combination sor,. alpha-hyphen-alpha whereas the Collections sort is straight alpha.

I’m not an expert on Java/Groovy sorting. These guys may be able to help:

@Brandon_Hein @kazurayam

Collections.sort() for Strings is based on the ASCII code of each character in the String. That’s why you get this sorting:

AA-1
AA-10
AA-2 (1)

instead of:

AA-1
AA-2 (1)
AA-10

because it’s not read as “2” vs. “10”, but rather “2” vs. “1” with some chars after…

You will likely need to implement a custom Comparator to do this.

1 Like

I have developed an example Comparator class; published at


Getting started

Try running Test Cases/main/TC1

import com.kazurayam.katalonforum.CustomStringComparator

import java.util.Arrays
import java.util.Collections
import java.util.List
import java.util.regex.Pattern
import java.util.regex.Matcher

String[] arr = ["AA-1", "AA-2", "AA-2 (1)", "AA-10"]

List expected = Arrays.asList(arr)
assert expected != null
assert expected[0] == "AA-1"
assert expected[1] == "AA-2"
assert expected[2] == "AA-2 (1)"
assert expected[3] == "AA-10"

List sorted1 = Arrays.asList(arr)
Collections.sort(sorted1)
assert sorted1 != null
assert sorted1[0] == "AA-1"
assert sorted1[1] == "AA-10"
assert sorted1[2] == "AA-2"
assert sorted1[3] == "AA-2 (1)"


List sorted2 = Arrays.asList(arr)
Collections.sort(sorted2, new CustomStringComparator())
assert sorted2 != null
assert sorted2[0] == "AA-1"
assert sorted2[1] == "AA-2"
assert sorted2[2] == "AA-2 (1)"
assert sorted2[3] == "AA-10"

where you can see a set of sample code working.

  • given with an array of String: ["AA-1", "AA-2", "AA-2 (1)", "AA-10"]
  • how Collections.sort() without Comparator works
  • and how Collections.sort() with CustomStringComparator works

You can find the source of com.kazurayam.katalonforum.CustomStringComparator class here.

My opinion

Developing com.kazurayam.katalonforum.CustomStringComparator class required seasoned skill of Java/Groovy programming. It involved unit-tests using JUnit. This exercises would require a few years of intensive Java programming lessons.

I think that it is too much for a UI tester; a UI tester should not be responsible for verifying the data-sorting algorithms. I would rather rely on the server-side developers to test the AUT thoroughly enough.

One thing I will say, if you know what the list should look like before hand, and that list doesn’t change between any two executions of the same test, you could:

1.) Build the “expected” list by hand, with the expected order.
2.) Sort the column in the application
3.) Get the “actual” list from the column in the application, after sorting.
4.) Compare

But I imagine that the list is probably not static.

or 5. sort both list (expected and actual) before to compare them

ooor, we can do some ‘abomination’ wrt code optimization, without sorting:

  • check both lists have the same length.
  • check every element in list A is present in list B
  • check each element in list B is present in list A

Yes, the list is not static.

Summary

Thank you for this! The code seems too complex, I am not that very good in Java/Groovy programming that’s why I do not understand it very well. This is the code structure that I am thinking that might work to verify the sorting feature.

public void sort (String xpath_id, String sort) {

Click the column header

	String tableId = driver.findElement(By.xpath('//div[@class='table']')).getAttribute('id')
	List<WebElement> tableElements = driver.findElements(By.xpath('//td[@class = 'tableId+'_' +xpath_id']'))

	ArrayList<String> expectedTableValues = new ArrayList<String>() // expected results (sorted)
	ArrayList<String> actualTableValues = new ArrayList<String>() // current table values
	//For loop to get the sorted column's data
	for (int i = 0; i < tableElements.size(); i++) {
		String currentRowValue = tableElements.get(i).getText() //get the text on the current row
		actualTableValues.add(currentRowValue) 
		expectedTableValues.add(currentRowValue)
		KeywordUtil.logInfo('CurrentRowDisplayed: ' +currentRowValue) 
	}

	switch (sort){
		case 'ASC':
			Collections.sort(expectedTableValues, String.CASE_INSENSITIVE_ORDER) //sort the 'tableValues' array list - this will serve as expected result
			break
		case 'DESC':
			Collections.sort(expectedTableValues, Collections.reverseOrder(String.CASE_INSENSITIVE_ORDER)) //sort the 'tableValues' array list - this will serve as expected result
			break
	}

	for (int i = 0; i < actualTableValues.size(); i++) {
		String actualValue = actualTableValues.get(i) 
		String expectedValue= expectedTableValues.get(i) 

		KeywordUtil.logInfo('Sorted: ' + expectedValue) //display the current data of sorted arraylist
		WebUI.VerifyEqual(actualValue,expectedValue)
	}}

This would invalidate the comparison though. OP is trying to validate that the column sorted the results correctly.

hi,

in python this is done in a few line
from natsort import natsorted

names = [‘AA-1’, ‘AA-10’, ‘AA-2’, ‘AA-2 (1)’]
print(natsorted(names, key=lambda y: y.lower()))

[‘AA-1’, ‘AA-2’, ‘AA-2 (1)’, ‘AA-10’]

will try to find out how to call python from java with arguments

and this java will do it too

import java.text.Collator;
import java.text.RuleBasedCollator;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

import static java.util.Collections.shuffle;
import static java.util.stream.Collectors.joining;

        List<String> list = new ArrayList<String>();
        list.add("AA-10");
        list.add("AA-1");
        list.add("AA-2 (1)");
        list.add("AA-2");

        RuleBasedCollator localRules = (RuleBasedCollator) Collator.getInstance();
        String extraRules = IntStream.range(0, 100).mapToObj(String::valueOf).collect(joining(" < "));
        RuleBasedCollator c = new RuleBasedCollator(localRules.getRules() + " & " + extraRules);

        shuffle(list);
        list.sort(c);
        System.out.println(list);

[AA-1, AA-2, AA-2 (1), AA-10]

1 Like

@Timo_Kuisma1

Thank you for your code. I learned it. I did not know Collator.

I rewrote your Java code into Groovy, which is runnable as a TestCase in Katalon Studio.

import static java.util.Collections.shuffle;
import static java.util.stream.Collectors.joining;
import java.text.Collator;
import java.text.RuleBasedCollator;
import java.util.stream.IntStream;

List<String> list = new ArrayList<String>();
list.add("AA-10");
list.add("AA-1");
list.add("AA-2 (1)");
list.add("AA-2");

RuleBasedCollator localRules = (RuleBasedCollator) Collator.getInstance();
String extraRules = IntStream.range(0, 100).mapToObj(String.&valueOf).collect(joining(" < "));
println "extraRules=" + extraRules
RuleBasedCollator c = new RuleBasedCollator(localRules.getRules() + " & " + extraRules);
shuffle(list);
list.sort(c);
System.out.println(list);
// [AA-1, AA-2, AA-2 (1), AA-10]

I changed String::valueOf to String.&valueOf.

1 Like