Returning a mix of items from a keyword

I need to return multiple values of different types from a function, was using Can a Custom Keyword return 2 or more values? Pls advice on the syntax as a guide but it doesn’t seem to cover what I’m trying to do (and it’s a Monday morning, might just need more coffee).

So I have the following function:

	public List<WebElement> validate_table_rows(TestObject table, int expected_row_count, String row_match_condition = "EQUALS", int object_timeout) {

		int header_count;
		int footer_count;
		List<WebElement>headers;
		List<WebElement>footers;
		List<WebElement>body_rows;

		if (!match_conditions.contains(row_match_condition)) {
			KeywordUtil.markFailedAndStop(row_match_condition + ' is not an acceptable parameter for the row match condition. Please enter EQUALS or MIN.')
		}

		WebElement table_element = WebUI.findWebElement(table, object_timeout);
		String table_id = table_element.getAttribute('id')

		List<WebElement> table_rows = table_element.findElements(By.tagName('tr'))
		table_row_count = table_rows.size()

		header_count = table_headers(table_id)
		footer_count = table_footers(table_id)
		body_rows = table_body(table_id)


		if (row_match_condition == "EQUALS") {
			if (table_row_count != expected_row_count) {
				KeywordUtil.markFailed('The expected row count is ' + expected_row_count + '. The actual row count is ' + table_row_count + '.')
			}
		}
		if (row_match_condition == "MIN") {
			if (table_row_count < expected_row_count) {
				KeywordUtil.markFailed('The expected row count is a minimum of ' + expected_row_count + '. The actual row count is ' + table_row_count + '.')
			}
		}

		KeywordUtil.logInfo('There are ' + table_row_count + ' rows. ' + header_count + ' of these rows are headers. ' + footer_count + ' of these rows are footers. The expected row count is ' + row_match_condition + ': ' + expected_row_count)

		return body_rows

	}

Which is currently called by

	public boolean validate_table_populated(String table_name, TestObject table, int expected_row_count, int expected_column_count, int stop_at_row = 50, String row_match_condition = "EQUALS", String column_match_condition = "EQUALS", int object_timeout = 20) {

		List<WebElement> table_columns;
		List<WebElement>table_rows = validate_table_rows(table, expected_row_count, row_match_condition, object_timeout)

		table_row_count = table_rows.size()

		if (table_row_count > stop_at_row) {
			table_row_count = stop_at_row
		}

		for(current_row = 0; current_row < table_row_count; current_row++) {
			log.logInfo("validate_table_populated - current table row: " + current_row)

			table_columns = validate_table_columns(table_rows.get(current_row), expected_column_count, current_row, column_match_condition)
			table_column_count = table_columns.size()
			log.logInfo("validate_table_populated -> table_column_count: " +table_column_count)

			for (int current_column = 0; current_column < table_column_count; current_column++) {

				String populated = validate_table_cell(table_name, table_columns, current_column)

			}
		}

		KeywordUtil.markPassed(table_row_count + ' rows have been validated in the ' + table_name + ' table body.')

		return true
	}

Note that the current return on the second function is always true for the time being.

What I need to add is a boolean return condition to the first function as well, so that if the markFailed item in the first function is triggered then I can have the first function return both the element list AND a boolean value of false in order to do some additional processing in the second function, but I’m not quite sure how to setup my return array to be able to handle both a LIST variable and a boolean on the return, can a basic array handle both or would I need to perform some more advanced configuration?

2 Likes

You can quickly invent a type to return, which contains any information you want to carry.

class TableRowsValidationResult {
    boolean condition
    List<WebElement> webElements
    TabelRowValidationResult(boolean condition, List<WebElement> webElements) {
        this.condition = condtion
        this.webElements
    }
}
public TableRowsValidationResult validate_table_rows(....) {
    ....
    return new TableRowsValidationResult(conditon, webElements)
}
public boolean validate_table_populated(....) {
    ....
    TableRowsValidationResult result = validate_table_rows(....)

    if (result.condition) {
        // when succeded
        List<WebElement> webElements = result.webElements
        for (WebElement we : webElements) {
            // do something on the web elements
        }
    } else {
        // when failed
    }
}
2 Likes

No, you don’t need that. SEPARATE out the two jobs that the first method is conducting currently.

boolean contains_match_condition = contains_condition(...)
list = validate_table_rows(...)
boolean validate_table_populated(...)

99.99% of your code should be written so that each method you write does ONE THING ALONE and does that thing CORRECTLY.

3 Likes

Let me propose an alternative solution. Have a look at the following Test Case:

def validate_table_rows() {
	return [true, ["Merry", "Christmas"]];
}

(b, l) = validate_table_rows();

println b
println l

When I run this, I saw the following messages in the console:

2023-12-20 07:38:57.055 DEBUG testcase.TC1                             - 2: println(b)
true
2023-12-20 07:38:57.061 DEBUG testcase.TC1                             - 3: println(l)
[Merry, Christmas]

It just works.

The function retuns a List of 2 elements; a Boolean value and a List of String.

The caller script invokes the function, get the returned List. And it quickly decomposes the returned List into elements b and l. The following statement does it:

(b, l) = ....

Please note that I wrapped the variable b and l with a pair of ( parenthis ). This is a valid syntax of Groovy language. The documentation calls this syntax as multiple assignment. See the doc

https://groovy-lang.org/semantics.html#_multiple_assignment

The above example shows returning/assigning 2 values; but you can do the same for 3, 4 and more values.

1 Like