Comparing two tables

I have Two tables on two different pages I need to take all the elements from one and then check if all these elements are available in the second table but they are not in the same order as the first one so what can i do ?

To compare 2 HTML tables, you want to

  1. create a Groovy class, namely my.Record, which represents a single row of <TABLE> in HTML. The class must implement boolean equals() method. The class must implement Comparable interface and overide int compare(Object, Object) method.
  2. extract all data out of the 1st table into an instance of java.util.List<List<Record>>
  3. extract all data out of the 2nd table into an instance of java.util.List<List<Record>> as well

Then you have options what to do next:

A.
4. sort the entries of the 1st List with appropriate column(s) as key
5. sort the entries of the 2nd List with appropriate column(s) as key
6. the rule of sorting must the same
7. then you will be ready to ā€œcompareā€ the 2 List objects.
8. How to compare them? I donā€™t know because you havenā€™t described how you want to.

B.
4. Iterate over the entries of List1 while checking the counter part entry is found in the List2.
5. How to check? I donā€™t know because you havenā€™t specified how to.

As you see, comparing 2 HTML <TABLE>s entirely require serious Java/Groovy programming. It is a difficult task to achieve. Do you really need to implement ā€œcomparing two tablesā€? I would recommend you not to go forward it.

You should rather find some relaxed criteria which is easier to implement and effective enough. For example, you just want to count the number of <TR> tags of <TABLE> using WebUI.findWebElements(TestObject, int) keyword; if the # of rows of 2 tables are identical, you say PASS! Or, you want to identify a single column which contains key that identifies each rows unique, then compare the set of the keys of Table 1 and Table2. If the 2 sets are equal, then you would say PASS! Here you would disregard other columns.

I need to compare the elements in the tables for examples:
in table 1 I have the Names of the customers and which department they are
table 2 I also have the same but with other options too

I want to check that my list in table 1 is all available and equal to the list of table 2 without having anyone missing or anyone added in both tables ( all the name should be available and only theses names/ departments should be present)
i was so far able to get all the elements from these tables all i need to know now is how to put them in a list ( Two by two : name-department for ex: Gio-Marketing) and compare them to the other table and make sure they are all available and not missing

OK, then, please show that data to us.

There is no magic that solves your problem out of box. You need to write fair amount of Groovy code.

Please make a new Test Case script which contains a set of your data as String literals. We do not need ā€œallā€ data. Several rows (say, 4?) would be enough as example. Possiblly the data will be contained in String or List<String> with variable names you like and data types you understand.

Provided with such simple code, then we would be able to start discussing how to write code to compare 2 data set. Unless provided a simple code, we would not be able to discuss about anything.

Capture

As you can see i have two similar Tables.
Table 1 is the Reference table We need to collect All data from it to know which customer belongs to and which branch then collect the Data From Table 2 make sure all elements in Table 1 are available in table 2 (not missing or anything added ) and the same Customer/ branch combo for example : Customer A: Retail B Should be available in Table 2 as Customer A: Retail B without taking into consideration their position in the table just if they both are there together to make sure all data is similar on both sides

Please provide the sample ā€œGroovyā€ code, not image of Excel.

ā€˜Expected value from Tableā€™
String ExpectedValue = ā€˜Customer Aā€™

ā€˜To locate tableā€™
WebElement Table = driver.findElement(By.className(ā€˜tableā€™))

ā€˜To locate rows of table it will Capture all the rows available in the tableā€™
List rows_table = Table.findElements(By.tagName(ā€˜trā€™))

ā€˜To calculate no of rows In tableā€™
int rows_count = rows_table.size()

ā€˜Loop will execute for all the rows of the tableā€™
Loop: for (int row = 0; row < rows_count; row++) {
ā€˜To locate columns(cells) of that specific rowā€™
List Columns_row = rows_table.get(row).findElements(By.tagName(ā€˜tdā€™))

'To calculate no of columns(cells) In that specific row'
int columns_count = Columns_row.size()

//println((('Number of cells In Row ' + row) + ' are ') + columns_count)
'Loop will execute till the last cell of that specific row'

for (int column = 0; column < columns_count-1; column++) {
    'It will retrieve text from each cell'
    String celltext = Columns_row.get(column).getText()
			
   println((((('Cell Value Of row number ' + row) + ' and column number ') + column) + ' is ') + celltext)

    'Checking if Cell text is matching with the expected value'
    if (celltext == ExpectedValue) {
        'Getting the Name if cell text i.e Customer name matches with Expected value'
       println('Text present in row number 3 is: ' + Columns_row.get(2).getText())

        'After getting the Expected value from Table we will Terminate the loop'
        Loop: break
	
    }
}

}

Iā€™m doing this twice on each table I have because they are both on different pages
i only need to compare the data and sort them as i mentioned before

Thank you disclosing your code. It proves your programming capability. At least you understand List data type and for loops.

Still I am wondering what are finding difficulty with.

how to Sort them two by two (customer/ channel) compare them to ( customer/ channel) other table i donā€™t want them one by one they come together as a combo lets say

My sample implementation is here.

import com.kms.katalon.core.util.KeywordUtil

/**
 * http://forum.katalon.com/t/comparing-two-tables/62051
 */

List<List<String>> input1 = [
		[ "Customer A", "Retail B", "x" ],
		// [ "Customer B", "Retail A", "x" ],
		[ "Customer C", "Retail B", "x" ],
		[ "Customer D", "Key Account", "x" ],
		[ "Customer E", "Retail A", "x" ],
		[ "Customer F", "Key Account", "x" ],
		[ "Customer G", "Key Account", "x" ],
];

List<List<String>> input2 = [
		[ "Customer D", "Key Account" ],
		[ "Customer B", "Retail A" ],
		[ "Customer C", "Retail B" ],
		[ "Customer A", "Retail B" ],
		[ "Customer G", "Key Account" ],
		[ "Customer E", "Retail A" ],
		// [ "Customer F", "Key Account" ],
];

Map<String, Record> table1 = new TreeMap<>();
for (List<String> row in input1) {
	Record rc = new Record(row[0], row[1], row[2]);
	table1.put(rc.key(), rc);
}
Set<String> keySet1 = table1.keySet()
//println "keySet1: " + keySet1

Map<String, Record> table2 = new TreeMap<>();
for (List<String> row in input2) {
	Record rc = new Record(row[0], row[1]);
	table2.put(rc.key(), rc);
}
Set<String> keySet2 = table2.keySet()
//println "keySet2: " + keySet2

// print the tables
println "-------- table 1 --------"
for (String key in keySet1) {
	println table1.get(key)
}
println ""
println "-------- table 2 --------"
for (String key in keySet2) {
	println table2.get(key)
}
println ""

// comparing the key set of the input1 and the input2
Set<String> w1 = new TreeSet<>(keySet1);
Set<String> w2 = new TreeSet<>(keySet2);
w1.removeAll(w2)
if (w1.size() > 0) {
	for (String k1 in w1) {
		System.err.println "\"${k1}\" is contained in the input1, but is missing in the input2"
		
	}
	KeywordUtil.markFailed("input1 > input2")
}

println ""

w1 = new TreeSet<>(keySet1);
w2 = new TreeSet<>(keySet2);
w2.removeAll(w1)
if (w2.size() > 0) {
	for (String k2 in w2) {
		System.err.println "\"${k2}\" is contained in the input2, but is missing in the input1"
	}
	KeywordUtil.markFailed("input1 < input2")
}


/**
 *
 */
class Record implements Comparable<Record> {
	private final String CUSTOMER;
	private final String CHANNEL;
	private final String BRANCHES;
	Record(String customer, String channel) {
		this(customer, channel, '');
	}
	Record(String customer, String channel, String branches) {
		this.CUSTOMER = customer.trim();
		this.CHANNEL = channel.trim();
		this.BRANCHES = branches.trim();
	}
	String CUSTOMER() {
		return this.CUSTOMER;
	}
	String CHANNEL() {
		return this.CHANNEL;
	}
	String BRANCHES() {
		return this.BRANCHES;
	}
	String key() {
		return this.CUSTOMER() + "|" + this.CHANNEL()
	}

	@Override
	boolean equals(Object obj) {
		if (! obj instanceof Record) {
			return false;
		}
		Record other = (Record)obj;
		return this.CUSTOMER() == other.CUSTOMER() &&
				this.CHANNEL() == other.CHANNEL() &&
				this.BRANCHES() == other.BRANCHES()
	}

	@Override
	int hashCode() {
		int hash = 7;
		hash = 31 * hash + this.CUSTOMER().hashCode();
		hash = 31 * hash + this.CHANNEL().hashCode();
		hash = 31 * hash + this.BRANCHES().hashCode();
		return hash;
	}

	@Override
	String toString() {
		return "[" + this.CUSTOMER() + "|" + this.CHANNEL() + "|" + this.BRANCHES() + "]";
	}

	@Override
	int compareTo(Record other) {
		int result = this.CUSTOMER() <=> other.CUSTOMER();
		if (result != 0) {
			return result;
		} else {
			result = this.CHANNEL() <=> other.CHANNEL();
			if (result != 0) {
				return result;
			} else {
				result = this.BRANCHES() <=> other.BRANCHES();
				return result;
			}
		}
	}
}

GIO.groovy.txt (3.4 KB)

When I executed this, I got the following output in the console.

2022-01-31 20:09:43.232 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2022-01-31 20:09:43.237 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/GIO
-------- table 1 --------
[Customer A|Retail B|x]
[Customer C|Retail B|x]
[Customer D|Key Account|x]
[Customer E|Retail A|x]
[Customer F|Key Account|x]
[Customer G|Key Account|x]

-------- table 2 --------
[Customer A|Retail B|]
[Customer B|Retail A|]
[Customer C|Retail B|]
[Customer D|Key Account|]
[Customer E|Retail A|]
[Customer G|Key Account|]

"Customer F|Key Account" is contained in the input1, but is missing in the input2
2022-01-31 20:09:44.422 ERROR com.kms.katalon.core.util.KeywordUtil    - āŒ input1 > input2

"Customer B|Retail A" is contained in the input2, but is missing in the input1
2022-01-31 20:09:44.454 ERROR com.kms.katalon.core.util.KeywordUtil    - āŒ input < input2
2022-01-31 20:09:44.461 ERROR c.k.katalon.core.main.TestCaseExecutor   - āŒ Test Cases/GIO FAILED.
Reason:
com.kms.katalon.core.exception.StepFailedException: input1 > input2
	at com.kms.katalon.core.util.KeywordUtil.markFailed(KeywordUtil.java:19)
	at com.kms.katalon.core.util.KeywordUtil$markFailed.call(Unknown Source)
	at GIO.run(GIO:64)
	at com.kms.katalon.core.main.ScriptEngine.run(ScriptEngine.java:194)
	at com.kms.katalon.core.main.ScriptEngine.runScriptAsRawText(ScriptEngine.java:119)
	at com.kms.katalon.core.main.TestCaseExecutor.runScript(TestCaseExecutor.java:442)
	at com.kms.katalon.core.main.TestCaseExecutor.doExecute(TestCaseExecutor.java:433)
	at com.kms.katalon.core.main.TestCaseExecutor.processExecutionPhase(TestCaseExecutor.java:412)
	at com.kms.katalon.core.main.TestCaseExecutor.accessMainPhase(TestCaseExecutor.java:404)
	at com.kms.katalon.core.main.TestCaseExecutor.execute(TestCaseExecutor.java:281)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:142)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:133)
	at com.kms.katalon.core.main.TestCaseMain$runTestCase$0.call(Unknown Source)
	at TempTestCase1643627379870.run(TempTestCase1643627379870.groovy:25)

2022-01-31 20:09:44.490 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/GIO

By the way, I developed this Groovy code in IntelliJ IDEA. The code was complexed enough so that I needed IDEAā€™s coding assistance.

1 Like

Thank you this helps a lot but there is still the problem that I am getting the items from a list while here u already had them custom made and inputted them Two by two, I just need to find a way to do that automatically while Iā€™m getting them from the web page into the list
P.S: Iā€™m doing them one by one (row by row and column by column according to the code I sent before )

of-course this is how to do it. however, i donā€™t fully understand the real case.
you have two lists, A and B.

  • you need to check if all elements in A are in B? easy, iterate over list A and check if list B contains the item.
  • you want to check if they are ā€˜sameā€™? compare the size of A and B first, if the size is different, something is wrong from the beginning, if the size is the same, do the previous (no need for reverse search). ideally, the lists should not contain duplicates, otherwise you will have to filter them first (no need to sort)
  • if still in doubt, sort both lists, using the same criteria, make sure the columns are also fit, and compare as a raw table (the most unpredictable solution, i donā€™t recommend it)

for anything else, you have to diff them, good luck with that!

I have two list A and B but each element in the list Iā€™m made of two elements example customer/channel
these two are different elements in the table but need to sort them two by two or row by row to be exact
each row should be the same as another row in the other table no matter of its position in the table as long as it is there

so, you are screwed. the only thing i can think right now is, import your two sources list into a similar sql tables, do the needful to bring them ad a decent comparison level and apply the needed filters.

sorry, too much bias into your issue ā€¦ simple lists i donā€™t think will help you, but a complex sql-like model

1 Like

I agree with @anon46315158.

Please have a look at my sample implementation. I introduced several programming techniques.

  1. class Record to encapsulate data in a row of HTML table
  2. the Record has getKey() method that makes a combination of CUTOMER+CHANNEL as the key which identifies a row of HTML table uniquely.
  3. use java.util.TreeMap to make a set of Record objects automatically sorted by the key; the Record class implements int compareTo(Record) method.
  4. use java.util.Set that provides removeAll() method which performs substitute operation of mathmatical set; the Record class implements boolean equals(Object) method.

@GIO

Do you think this is unnecessarily complicated? I donā€™t think so. This sample code is an absolute minimum to solve the problem that @GIO presented. His problem is so difficult that it deserves this complexity.

However, I think that it is not a good idea to perform such full stack programming in an automated UI tests. I think such full stack programming should be carried out in the unit-tests for the web server app possibly using SQL; or the unit-tests for the JavaScript upon JSON data in the single page web app. I think automated UI test for HTML should NOT be responsible for this layer of problems. Do you remember? I wrote,

I suppose, a complexed UI automation code will not be well maintained long. Sooner or later (in a few months) it will break when the HTML design changed. Then you will remove the broken UI test out of the Test Suite.

1 Like

Well thank you i guess I will try to find a solution in any way and ill come back here for more questions if needed !

Agreed.
Data validation should be subject for unit-testing for model validation and separate functional tests for DB functionality.
So, later in UI testing part, you only have to use smaller samples of data, whatever is relevant (positive workflows mostly, unless you have to check also some data sanitizing features, however the data sample will be smaller).
Therefore, the End-to-End part will consist of various modules (db test, APIā€™s test, UI tests) put together in a nice pipeline.

I found a solution for it
You just loop through the table get the first column element in the row then add it to is as a string from the second column item and I concatenate them together:

listOne.add(celltext1+ā€™ '+Columns_row.get(1).getText());
celltext1 is the first row element and i add the second one manually ( i did it manually and specified which column i want to take the second items with because they are all same Column)
if u want more details or my full code concerning that matter let me know

1 Like

as long as it works for you, i donā€™t wanā€™t to see your code :smiley:
otherwise I may ask ā€˜kindly ship your machineā€™ ā€¦