Dynamic IDs are breaking my tests every time I refresh

Hi everyone, I’m really new to Katalon Studio and I’m hitting a wall with the login page of my web app. I used the Record Web tool to create a simple login script, and it worked perfectly the first time. But when I try to run it again, or if I just refresh the page, the test fails with a Step Failed: Object 'Object Repository/Login_Button' not found error.

I looked at the HTML, and it seems like the ID for the login button is something like btn-5821 one minute, and then btn-9374 the next. Since Katalon recorded it using that specific ID, it can’t find it once the numbers change. I tried manually editing the object in the Object Repository to use a different attribute, but I’m not sure which one is “safe” to use or how to make the selector more flexible. It feels like I’m chasing a moving target—how do I get Katalon to recognize the button even when the ID keeps changing?

1 Like

Hi there, and thanks for posting in the Katalon community! :hugs:

To help you faster, please review our guide on Record Web Utility here: Record Web utility in Katalon Studio | Katalon Docs. Double-checking the steps and configurations might resolve the issue.

If the doc doesn’t help, feel free to provide more details, and a community member will assist you soon.

Thanks for being a part of our community!
Best,
Elly Tran

show us the html code of that button pls and we will help you , it seems you are not using the correct locator

1 Like

What you are encountering is a classic “Dynamic Attribute” issue. Developers often use frameworks (like React, Angular, or Vue) that auto-generate IDs during the build or render process. Relying on these IDs is a recipe for brittle tests.

To build a robust and scalable framework, we shift away from “Recorded” absolute attributes and move toward Relative XPaths or Parametrized Selectors. Instead of looking for a random number, we look for stable “anchors” like text labels, class names, or hierarchical relationships.

The Solution: XPath Optimization

Instead of using an ID that looks like //button[@id='btn-1234'], we use a partial match or a text-based match. This ensures that as long as the button says “Login,” the test will pass.

  1. Open your Object Repository and select the failing object.

  2. Switch the Detection Method to XPath.

  3. Update the XPath to one of the following stable patterns:

    • Text-based: //button[text()='Login']

    • Partial Attribute: //button[contains(@id, 'btn-')]

    • Class & Text combo: //button[contains(@class, 'btn-primary') and .='Login']

Custom Keyword: The “Smart Finder”

To make your life easier, you can create a Custom Keyword that finds an element by its text regardless of its changing ID. This makes your scripts much more readable and maintainable.

Groovy

package com.helpers

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

public class SmartElementFinder {

    /**
     * Finds a button by its visible text, ignoring dynamic IDs
     */
    @Keyword
    def clickButtonByText(String buttonText) {
        TestObject dynamicObject = new TestObject("DynamicButton")
        // Create an XPath that looks for a button containing the specific text
        String xpath = "//button[normalize-space()='" + buttonText + "']"
        
        dynamicObject.addProperty("xpath", ConditionType.EQUALS, xpath)
        
        WebUI.waitForElementVisible(dynamicObject, 5)
        WebUI.click(dynamicObject)
    }
}

Implementation Strategy:

  • Avoid the Recorder for IDs: When recording, if you see an ID with long strings of numbers, immediately flag it.

  • Use Data Attributes: If you have influence over the development team, ask them to add data-testid="login-button". These attributes are ignored by CSS/JS and are dedicated solely to making your automation stable.

  • The “Normalize-Space” Trick: Use normalize-space() in your XPaths to ignore weird gaps or tabs that developers sometimes leave around the button text.

By implementing these selector strategies, your test suite will remain stable across different environments and UI refreshes.

4 Likes

Solution for the difficulty caused by dynamic id entirely depends on how the target HTML is written. If you want the guys in this forum to answer to your question, you should disclose the HTML source of your target.

To view the DOM nodes, use Chrome DevTools.

if you go to Project > Settings > Test Design > Web UI, you can change the priority of your pathway locators. Jump over to the Attribute tab and remove the check for the <id> attribute; It was the <id> attribute that was the main dynamic reference within our software. Try this for a bit.
For me, I eventually put the checkmark back and then modified the pathway using “contains” and “starts-with”, like @qurzunta.kaab states.

//a[contains(@id, 'mainmenu') and contains(@id,'CashAndBankManagement') and contains(@id,'CashFlowForecasting')]

//input[starts-with(@id,'DirPartyQuickCreateForm_') and contains(@id, 'DynamicDetail_CustGroup_input')]/following-sibling::div/div[@class='lookupButton']

//input[contains(@id,'ledgercalendars_') and contains(@id, '_YearGroup_StartDate_input')]
3 Likes

Thanks, That a nice hack!

2 Likes

Dynamic IDs (btn-5821 → btn-9374) kill recorded tests—switch to stable XPath ignoring numbers.

Robust Selectors

Object Repo > Login_Button → Edit XPath:

1. Text: //button[normalize-space(text())='Login']  // Exact match
2. Contains ID: //button[contains(@id, 'btn-')]     // Partial
3. Class+Text: //button[contains(@class, 'login') and text()='Login']

Custom Smart Click:

@Keyword
void clickByText(String text) {
    TestObject btn = new TestObject('dynamic')
    btn.addProperty('xpath', ConditionType.EQUALS, "//button[.='${text}']")
    WebUI.click(btn)
}

CustomKeywords.clickByText('Login')—page refresh proof!

Ask devs for data-testid="login-btn"—automation gold.

If you want to learn about working with test objects using Studio, we got a learning path for that.

2 Likes

thanks i am following these

this fixed on one part i also need help with some other webcomponents can you answer my query with some code that solve it