For Loop Fails and Crashes Test when Encountering Already Checked Box

Hey everyone, I’m pretty new to Katalon Studio and automation in general, and I’ve been stuck on this for two days now.

I’m trying to automate a page that has a long list of checkboxes. My goal is to loop through all of them and click them to make sure they are all selected. I recorded the objects and set up a standard for loop in Script view using WebUI.click().

It works perfectly if all the checkboxes are unchecked. But if the script runs into a checkbox that someone (or a previous test) already checked, the test execution just stops right there with an error, and the rest of my test suite fails. I tried adding a delay, and I even tried to manually re-record the elements, but nothing works. How do I make Katalon smart enough to check if it’s already ticked, skip it if it is, and keep moving down the list without crashing? Any help would be appreciated!"

The Core Issue

The problem occurs because your script lacks conditional logic and proper failure handling. When you blindly execute WebUI.click() on an already selected checkbox, one of two things happens depending on your site’s architecture:

  1. It unchecks the box (violating your test’s intent to ensure all are checked).

  2. It throws an element state exception or fails a strict verification step, which by default instructs Katalon Studio to halt the entire test execution.

To build a robust, scalable test, you must first verify the state of the checkbox using WebUI.verifyElementChecked or WebUI.getAttribute before interacting with it. Furthermore, you must change the default FailureHandling behavior so that an unexpected state doesn’t kill the entire loop.

The Solution

  1. Check the State First: Use WebUI.verifyElementChecked() with a brief timeout.

  2. Apply Optional Failure Handling: Pass FailureHandling.OPTIONAL as an argument. This tells Katalon, “If this check fails (i.e., the box is NOT checked), do not stop the test—just move to the next line.”

  3. Conditional Execution: Wrap the click action inside a logic block so it only fires when necessary.

Custom Keyword Implementation

Instead of cluttering your main test scripts with complex loop logic, the industry standard is to encapsulate this behavior into a reusable Custom Keyword. This allows any tester on your team to check a list of checkboxes safely with a single line of code.

Here is the custom keyword solution.

1. Create the Custom Keyword

In Katalon Studio, navigate to Keywords, create a new Package (e.g., com.helper), create a new Keyword class (e.g., CheckboxUtils), and paste the following code:

Groovy

package com.helper

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.util.KeywordUtil
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

public class CheckboxUtils {

	/**
	 * Safely loops through a list of checkbox TestObjects and ensures they are checked.
	 * If a checkbox is already checked, it skips it smoothly.
	 * * @param checkboxObjects A List of TestObjects representing the checkboxes
	 */
	@Keyword
	def void ensureAllCheckboxesAreChecked(List<TestObject> checkboxObjects) {
		for (int i = 0; i < checkboxObjects.size(); i++) {
			TestObject checkbox = checkboxObjects.get(i)
			
			// Check if the element is visible/present first
			if (WebUI.waitForElementVisible(checkbox, 5, FailureHandling.OPTIONAL)) {
				
				// Verify if checked. Using OPTIONAL prevents the test from stopping if it returns false.
				boolean isChecked = WebUI.verifyElementChecked(checkbox, 2, FailureHandling.OPTIONAL)
				
				if (!isChecked) {
					WebUI.click(checkbox)
					KeywordUtil.logInfo("Checkbox at index " + i + " was unchecked. Clicked successfully.")
				} else {
					KeywordUtil.logInfo("Checkbox at index " + i + " is already checked. Skipping.")
				}
			} else {
				KeywordUtil.logWarning("Checkbox at index " + i + " was not visible within timeout limits.")
			}
		}
	}
}

2. How to Use It in Your Main Test Script (Script View)

Once the keyword is saved, you can call it elegantly in your main test case like this:

Groovy

import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.testobject.TestObject

// 1. Gather your checkbox Test Objects into a list
List<TestObject> myCheckboxes = [
	findTestObject('Object Repository/Page_Home/checkbox_1'),
	findTestObject('Object Repository/Page_Home/checkbox_2'),
	findTestObject('Object Repository/Page_Home/checkbox_3')
]

// 2. Call your custom keyword to handle the looping safely
CustomKeywords.'com.helper.CheckboxUtils.ensureAllCheckboxesAreChecked'(myCheckboxes)

script lacks conditional logic and failure handling. When you blindly click an already-selected checkbox, it throws an element state exception or unchecks the box, causing the test to halt.

The Solution: Check State First + Optional Failure Handling

Approach 1: Quick Fix in Script View

import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

// Loop through checkboxes
for (int i = 0; i < numberOfCheckboxes; i++) {
    TestObject checkbox = findTestObject('Object Repository/checkbox_' + i)
    
    // 1. Check if element is visible first
    if (WebUI.waitForElementVisible(checkbox, 5, FailureHandling.OPTIONAL)) {
        
        // 2. Verify if checked (OPTIONAL prevents test from stopping if false)
        boolean isChecked = WebUI.verifyElementChecked(checkbox, 2, FailureHandling.OPTIONAL)
        
        // 3. Only click if NOT checked
        if (!isChecked) {
            WebUI.click(checkbox)
            logInfo("Checkbox " + i + " was unchecked. Clicked successfully.")
        } else {
            logInfo("Checkbox " + i + " is already checked. Skipping.")
        }
    } else {
        logWarning("Checkbox " + i + " was not visible within timeout.")
    }
}

Approach 2: Production-Grade Custom Keyword (Recommended)

Create a reusable Custom Keyword to encapsulate this logic:

  1. Navigate to Keywords → New Package (com.helper) → New Keyword Class (CheckboxUtils)

  2. Paste this code:

package com.helper

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.util.KeywordUtil
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

public class CheckboxUtils {

/**
 * Safely loops through checkboxes and ensures they are checked.
 * Skips already-checked boxes without crashing.
 * 
 * @param checkboxObjects A List of TestObjects representing the checkboxes
 */
@Keyword
def void ensureAllCheckboxesAreChecked(List<TestObject> checkboxObjects) {
    for (int i = 0; i < checkboxObjects.size(); i++) {
        TestObject checkbox = checkboxObjects.get(i)
        
        // Check if element is visible/present first
        if (WebUI.waitForElementVisible(checkbox, 5, FailureHandling.OPTIONAL)) {
            
            // Verify if checked (OPTIONAL prevents test from stopping if false)
            boolean isChecked = WebUI.verifyElementChecked(checkbox, 2, FailureHandling.OPTIONAL)
            
            if (!isChecked) {
                WebUI.click(checkbox)
                KeywordUtil.logInfo("Checkbox at index " + i + " was unchecked. Clicked successfully.")
            } else {
                KeywordUtil.logInfo("Checkbox at index " + i + " is already checked. Skipping.")
            }
        } else {
            KeywordUtil.logWarning("Checkbox at index " + i + " was not visible within timeout limits.")
        }
    }
}
}

How to Use the Custom Keyword

**import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.testobject.TestObject

// 1. Gather checkbox TestObjects into a list
List<TestObject> myCheckboxes = [
findTestObject('Object Repository/Page_Home/checkbox_1'),
findTestObject('Object Repository/Page_Home/checkbox_2'),
findTestObject('Object Repository/Page_Home/checkbox_3')
]

// 2. Call your custom keyword to handle looping safely
CustomKeywords.'com.helper.CheckboxUtils.ensureAllCheckboxesAreChecked'(myCheckboxes)**

Key Techniques Explained

Technique Why It Works
verifyElementChecked() first Checks state before clicking — prevents unchecking already-selected boxes
FailureHandling.OPTIONAL Tells Katalon: “If this check fails, don’t stop the test — just move to next line”
if (!isChecked) wrapper Only clicks when necessary — skips already-checked boxes
waitForElementVisible() Ensures element is present before attempting verification

Why Your Other Attempts Failed

Attempt Problem
Adding delay Doesn’t check state — still clicks blindly
Re-recording elements Doesn’t solve the logic issue — still no conditional check
No failure handling Default behavior halts entire test on any exception

Bottom line: Use verifyElementChecked() + FailureHandling.OPTIONAL to check state before clicking and prevent test crashes

I support using a Keyword like @qurzunta.kaab states, but I would only process one checkbox at a time so that you do not have a complex statement that has 20 checkboxes in them, or you are going to have to do the same statements for multiple checkboxes anyways as to not make your code too complex to understand 6 months from now. Yes, you will have more statements in your code, but it should be easier to understand at your level.

Maybe like:
@Keyword
def void ensureCheckboxisChecked(TestObject checkObject) {
		
	// Check if the element is visible/present first
	if (WebUI.waitForElementClickable(checkObject, 5, FailureHandling.OPTIONAL)) {
		// Verify if checked. Using OPTIONAL prevents the test from stopping if it returns false.
		boolean isChecked = WebUI.verifyElementChecked(checkObject, 3, FailureHandling.OPTIONAL)
			
		if (!isChecked) {
			WebUI.check(checkbox)
            if (WebUI.verifyElementNotChecked(checkObject, 3, FailureHandling.OPTIONAL){
                WebUI.comment('Checkbox is not checked!')
            }
		} 
	} 
}

Edit: I also think this will assist when the pathway to any one of the checkboxes changes, it will isolate the specific one that you can then repair.

Edit2: You can actually have both methods, the multiple checkbox and the single checkbox methods in your Keyword class with the SAME name, as long as you have different parameters. As written above, @qurzunta.kaab method has a list of checkboxes, and mine only has one checkbox Test Object, so the parameters are different even though the method names are the same. This is called Overloading and sure is neat. Your code will decide which method to use based on the parameters you use.

use WebUI.verifyElementChecked() with FailureHandling.OPTIONAL before clicking. The OPTIONAL failure handling is key; without it, a verification failure halts execution.

for (int i = 1; i <= count; i++) {
    TestObject cb = findTestObject('Object Repository/checkbox', [('index') : i])
    boolean isChecked = WebUI.verifyElementChecked(cb, 2, FailureHandling.OPTIONAL)
    if (!isChecked) {
        WebUI.click(cb)
    }
}

verifyElementChecked returns a boolean. When the box is already checked, it returns true and you skip the click. When it is not checked, it returns false (instead of crashing) because FailureHandling.OPTIONAL suppresses the exception. Then you click it.