Is there a way to mass import object or generate them via script?

Hello everyone,

I’ve been working on a project that has about 20+ pages each with 10 to 30 fields that i need to fill and test. We have implemented html id tags that I use to uniquely identify my object. My problem is that this takes me a lot of time just to generate my object repository and I’m looking for ways to speed this up.

I use relative Xpath to define all my objects. The structure of the xpath will vary depending on the type of field (radio-button, drop-down, date,…) but it is predictable. My xpath structure goes something like this:

//[@id = ‘Custom_id’]//[contains(@class, ‘class_of_my_object’)]

Is there any way I could either import all of my objects and have them with the select locator set to the xpath I have defined ? Or any way I can use a script to generate these object ? Right now I’m just creating them mostly through webspy + editing which takes a lot of time.

Thanks

1 Like

Hi there,

Thank you very much for your topic. Please note that it may take a little while before a member of our community or from Katalon team responds to you.

Thanks!

You do not have to create files in the “Object Resository” folder. You can create an instance of com.kms.katalon.core.testobject.TestObject from a string as XPath by a Groovy function runtime.

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

TestObject makeTestObjectXPath(String id, String xPath) {
	TestObject tObj = new TestObject(id)
	tObj.addProperty("xpath", ConditionType.EQUALS, xPath)
	return tObj
}

String url = 'https://react.dev/learn/typescript#typing-dom-events'

WebUI.openBrowser('')
WebUI.maximizeWindow()
WebUI.navigateToUrl(url)

// in the page, find the 5th div that contains 'App.tsx' 
// which is near the header string "DOM Events", locate the target <iframe> element
TestObject toIframe = makeTestObjectXPath('toIframe',
	"//div[contains(@class,'sandpack') and contains(.,'App.tsx')][5]//iframe")
WebUI.verifyElementPresent(toIframe, 10)

WebUI.closeBrowser()

You do not like to hard-code XPaths in your TestCase? — OK. You can create a text file which contains a lot of lines of XPath. Your test case can read the file, convert a line into a TestObject, and apply to WebUI.* keywords in your TestCase script.

Or, you may want to create another function(s) that generates Strings as XPath dynamically. If it helps you, it’s a good idea to make your project compact even more.

See also

The issue I see is that you are working with <id> and they are supposed to be unique. In other words, you will have to visit every pathway anyways to enter the unique <id>. What I did is to have a copy of the pathway, maybe in Notepad or such, beside my work and use that as a template for the xpath format for the objects. Now, my objects had dynamic <id>, and the front part was generally the same but the back part was different. So, I had to fix that up for all of the objects. It doesn’t save much time but I found it easier than retyping the pathway every time, or going into the HTML to copy/paste and then fix from there.
Edit: I also don’t use the Web Spy’s default name. I have developed my own naming convention to own my objects and identify them uniquely. Having a dozen Comment objects means you need to know the exact placement of each one and my naming of the objects does that (although I have had to lengthen the name with more content a couple of times as the project changed). Yes, it does take time, but that’s part of my position as Tester.

1 Like

I presented a sample Test Case script that has a function makeTestObject() declared inside the TestCase. This will work. But this way has a potential problem. If you call the makeTestObject() function repeatedly, say 100 times, you would find quite a lot of lines of DEBUG log in the LogViewer. See the following Test Case:

import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import org.openqa.selenium.WebElement

TestObject makeTestObjectXPath(String id, String xPath) {
	TestObject tObj = new TestObject(id)
	tObj.addProperty("xpath", ConditionType.EQUALS, xPath)
	return tObj
}

TestObject makeTestObjectCSS(String id, String selector) {
	TestObject tObj = new TestObject(id)
	tObj.addProperty("css", ConditionType.EQUALS, selector)
	return tObj
}

// set the path of Firefox binary
System.setProperty("webdriver.firefox.bin", "/Applications/Firefox.app/Contents/MacOS/firefox")

String url = 'https://react.dev/learn/typescript#typing-dom-events'

WebUI.openBrowser('')
WebUI.maximizeWindow()
WebUI.navigateToUrl(url)

// in the page, find the 5th div that contains 'App.tsx' 
// which is near the header string "DOM Events", locate the target <iframe> element
TestObject toIframe = makeTestObjectXPath('toIframe',
	"//div[contains(@class,'sandpack') and contains(.,'App.tsx')][5]//iframe")
WebUI.verifyElementPresent(toIframe, 10)

// switch the context into the <iframe>
WebUI.switchToDefaultContent()
boolean b = WebUI.switchToFrame(toIframe, 10, FailureHandling.STOP_ON_FAILURE)
assert b : 'failed to switch to the iframe'

// inside the <iframe>, make sure a HTML document has been loaded
TestObject toRoot = makeTestObjectXPath('toRoot', "/html/body")
WebUI.verifyElementPresent(toRoot, 10, FailureHandling.STOP_ON_FAILURE)

// locate the <input> element
TestObject toInput = makeTestObjectXPath('toInput',
	"//*[@id='root']/input")
WebUI.verifyElementPresent(toInput, 10, FailureHandling.STOP_ON_FAILURE)

WebUI.delay(3)

// try typing a string into the <input> element which was rendered by React.js
WebUI.clearText(toInput)
WebUI.sendKeys(toInput, "Hooah")

WebUI.delay(3)

// make sure that a text was echoed in the sibling <p> element 
TestObject toValue = makeTestObjectXPath('toValue',
	"//*[@id='root']/p")
WebUI.verifyElementText(toValue, 'Value: Hooah')

// Switch back to default content'
WebUI.switchToDefaultContent()

WebUI.closeBrowser()

This Test Case emits the following message in the console:

2024-08-26 06:41:23.542 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2024-08-26 06:41:23.549 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/TC1
2024-08-26 06:41:24.358 DEBUG testcase.TC1                             - 1: System.setProperty("webdriver.firefox.bin", "/Applications/Firefox.app/Contents/MacOS/firefox")
2024-08-26 06:41:24.377 DEBUG testcase.TC1                             - 2: url = "https://react.dev/learn/typescript#typing-dom-events"
2024-08-26 06:41:24.383 DEBUG testcase.TC1                             - 3: openBrowser("")
2024-08-26 06:41:24.987 INFO  c.k.k.core.webui.driver.DriverFactory    - Starting 'Chrome' driver
8月 26, 2024 6:41:24 午前 org.openqa.selenium.remote.DesiredCapabilities chrome
情報: Using `new ChromeOptions()` is preferred to `DesiredCapabilities.chrome()`
2024-08-26 06:41:25.047 INFO  c.k.k.core.webui.driver.DriverFactory    - Action delay is set to 0 milliseconds
Starting ChromeDriver 127.0.6533.119 (bdef6783a05f0b3f885591e7d2c7b2aec1a89dea-refs/branch-heads/6533@{#1999}) on port 10580
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
8月 26, 2024 6:41:29 午前 org.openqa.selenium.remote.ProtocolHandshake createSession
情報: Detected dialect: W3C
2024-08-26 06:41:29.212 INFO  c.k.k.core.webui.driver.DriverFactory    - sessionId = 618fe0111edc769e77303f4e55b81f20
2024-08-26 06:41:29.255 INFO  c.k.k.core.webui.driver.DriverFactory    - browser = Chrome 127.0.0.0
2024-08-26 06:41:29.276 INFO  c.k.k.core.webui.driver.DriverFactory    - platform = Mac OS X
2024-08-26 06:41:29.315 INFO  c.k.k.core.webui.driver.DriverFactory    - seleniumVersion = 3.141.59
2024-08-26 06:41:29.337 INFO  c.k.k.core.webui.driver.DriverFactory    - proxyInformation = ProxyInformation { proxyOption=NO_PROXY, proxyServerType=HTTP, username=, password=********, proxyServerAddress=, proxyServerPort=0, executionList="", isApplyToDesiredCapabilities=true }
2024-08-26 06:41:29.382 DEBUG testcase.TC1                             - 4: maximizeWindow()
2024-08-26 06:41:29.855 DEBUG testcase.TC1                             - 5: navigateToUrl(url)
2024-08-26 06:41:31.253 DEBUG testcase.TC1                             - 6: toIframe = makeTestObjectXPath("toIframe", "//div[contains(@class,'sandpack') and contains(.,'App.tsx')][5]//iframe")
2024-08-26 06:41:31.275 DEBUG testcase.TC1                             - 1: tObj = new com.kms.katalon.core.testobject.TestObject(id)
2024-08-26 06:41:31.317 DEBUG testcase.TC1                             - 2: tObj.addProperty("xpath", EQUALS, xPath)
2024-08-26 06:41:31.347 DEBUG testcase.TC1                             - 3: return tObj
2024-08-26 06:41:31.361 DEBUG testcase.TC1                             - 7: verifyElementPresent(toIframe, 10)
2024-08-26 06:41:32.912 DEBUG testcase.TC1                             - 8: switchToDefaultContent()
2024-08-26 06:41:33.006 DEBUG testcase.TC1                             - 9: b = switchToFrame(toIframe, 10, STOP_ON_FAILURE)
2024-08-26 06:41:33.518 DEBUG testcase.TC1                             - 10: assert b : "failed to switch to the iframe"
2024-08-26 06:41:33.530 DEBUG testcase.TC1                             - 11: toRoot = makeTestObjectXPath("toRoot", "/html/body")
2024-08-26 06:41:33.540 DEBUG testcase.TC1                             - 1: tObj = new com.kms.katalon.core.testobject.TestObject(id)
2024-08-26 06:41:33.543 DEBUG testcase.TC1                             - 2: tObj.addProperty("xpath", EQUALS, xPath)
2024-08-26 06:41:33.547 DEBUG testcase.TC1                             - 3: return tObj
2024-08-26 06:41:33.553 DEBUG testcase.TC1                             - 12: verifyElementPresent(toRoot, 10, STOP_ON_FAILURE)
2024-08-26 06:41:33.764 DEBUG testcase.TC1                             - 13: toInput = makeTestObjectXPath("toInput", "//*[@id='root']/input")
2024-08-26 06:41:33.767 DEBUG testcase.TC1                             - 1: tObj = new com.kms.katalon.core.testobject.TestObject(id)
2024-08-26 06:41:33.769 DEBUG testcase.TC1                             - 2: tObj.addProperty("xpath", EQUALS, xPath)
2024-08-26 06:41:33.778 DEBUG testcase.TC1                             - 3: return tObj
2024-08-26 06:41:33.785 DEBUG testcase.TC1                             - 14: verifyElementPresent(toInput, 10, STOP_ON_FAILURE)
2024-08-26 06:41:34.792 DEBUG testcase.TC1                             - 15: delay(3)
2024-08-26 06:41:37.889 DEBUG testcase.TC1                             - 16: clearText(toInput)
2024-08-26 06:41:38.053 DEBUG testcase.TC1                             - 17: sendKeys(toInput, "Hooah")
2024-08-26 06:41:38.651 DEBUG testcase.TC1                             - 18: delay(3)
2024-08-26 06:41:41.698 DEBUG testcase.TC1                             - 19: toValue = makeTestObjectXPath("toValue", "//*[@id='root']/p")
2024-08-26 06:41:41.702 DEBUG testcase.TC1                             - 1: tObj = new com.kms.katalon.core.testobject.TestObject(id)
2024-08-26 06:41:41.705 DEBUG testcase.TC1                             - 2: tObj.addProperty("xpath", EQUALS, xPath)
2024-08-26 06:41:41.707 DEBUG testcase.TC1                             - 3: return tObj
2024-08-26 06:41:41.719 DEBUG testcase.TC1                             - 20: verifyElementText(toValue, "Value: Hooah")
2024-08-26 06:41:41.841 DEBUG testcase.TC1                             - 21: switchToDefaultContent()
2024-08-26 06:41:41.872 DEBUG testcase.TC1                             - 22: closeBrowser()
2024-08-26 06:41:42.193 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

Please find that the following a section of 3 lines are repeated 4 times = the number of times the TestCase calls the function:

2024-08-26 06:41:41.702 DEBUG testcase.TC1                             - 1: tObj = new com.kms.katalon.core.testobject.TestObject(id)
2024-08-26 06:41:41.705 DEBUG testcase.TC1                             - 2: tObj.addProperty("xpath", EQUALS, xPath)
2024-08-26 06:41:41.707 DEBUG testcase.TC1                             - 3: return tObj

I find this 3 lines give me no useful information. I don’t need it.

This section will be repeated as per the times your TestCase calls the funtion. Consequently the amount of DEBUG logs gets bulky. The LogViewer becomes less useless. The test runs slower to flush the buffered messages into the UI window and log files. See the following previous post of mine:

How to get rid of the verbose log messages from the function that creates a TestObject dynamically?

I can show you an alternative. You can create a custom Keyword class which implements the function as its method. The inner steps of custom Keywords won’t be logged by the Katalon Logger.

"Keywords/my/TestObjectFactory"

package my

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject


public class TestObjectFactory {

	static TestObject makeTestObjectXPath(String id, String xPath) {
		TestObject tObj = new TestObject(id)
		tObj.addProperty("xpath", ConditionType.EQUALS, xPath)
		return tObj
	}

	static TestObject makeTestObjectCSS(String id, String selector) {
		TestObject tObj = new TestObject(id)
		tObj.addProperty("css", ConditionType.EQUALS, selector)
		return tObj
	}
}

Imporoved "Test Cases/TC1

import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import org.openqa.selenium.WebElement
import my.TestObjectFactory

// set the path of Firefox binary
System.setProperty("webdriver.firefox.bin", "/Applications/Firefox.app/Contents/MacOS/firefox")

String url = 'https://react.dev/learn/typescript#typing-dom-events'

WebUI.openBrowser('')
WebUI.maximizeWindow()
WebUI.navigateToUrl(url)

// in the page, find the 5th div that contains 'App.tsx' 
// which is near the header string "DOM Events", locate the target <iframe> element
TestObject toIframe = TestObjectFactory.makeTestObjectXPath('toIframe',
	"//div[contains(@class,'sandpack') and contains(.,'App.tsx')][5]//iframe")
WebUI.verifyElementPresent(toIframe, 10)

// switch the context into the <iframe>
WebUI.switchToDefaultContent()
boolean b = WebUI.switchToFrame(toIframe, 10, FailureHandling.STOP_ON_FAILURE)
assert b : 'failed to switch to the iframe'

// inside the <iframe>, make sure a HTML document has been loaded
TestObject toRoot = TestObjectFactory.makeTestObjectXPath('toRoot', "/html/body")
WebUI.verifyElementPresent(toRoot, 10, FailureHandling.STOP_ON_FAILURE)

// locate the <input> element
TestObject toInput = TestObjectFactory.makeTestObjectXPath('toInput',
	"//*[@id='root']/input")
WebUI.verifyElementPresent(toInput, 10, FailureHandling.STOP_ON_FAILURE)

WebUI.delay(3)

// try typing a string into the <input> element which was rendered by React.js
WebUI.clearText(toInput)
WebUI.sendKeys(toInput, "Hooah")

WebUI.delay(3)

// make sure that a text was echoed in the sibling <p> element 
TestObject toValue = TestObjectFactory.makeTestObjectXPath('toValue',
	"//*[@id='root']/p")
WebUI.verifyElementText(toValue, 'Value: Hooah')

// Switch back to default content'
WebUI.switchToDefaultContent()

WebUI.closeBrowser()

This improved version will emit the following messages in the LogViewer, which does not repeat the sections of useless 3 lines that I showed above.

2024-08-26 05:58:21.290 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2024-08-26 05:58:21.294 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/TC1
2024-08-26 05:58:21.990 DEBUG testcase.TC1                             - 1: System.setProperty("webdriver.firefox.bin", "/Applications/Firefox.app/Contents/MacOS/firefox")
2024-08-26 05:58:22.006 DEBUG testcase.TC1                             - 2: url = "https://react.dev/learn/typescript#typing-dom-events"
2024-08-26 05:58:22.066 DEBUG testcase.TC1                             - 3: openBrowser("")
2024-08-26 05:58:22.510 INFO  c.k.k.core.webui.driver.DriverFactory    - Starting 'Chrome' driver
8月 26, 2024 5:58:22 午前 org.openqa.selenium.remote.DesiredCapabilities chrome
情報: Using `new ChromeOptions()` is preferred to `DesiredCapabilities.chrome()`
2024-08-26 05:58:22.564 INFO  c.k.k.core.webui.driver.DriverFactory    - Action delay is set to 0 milliseconds
Starting ChromeDriver 127.0.6533.119 (bdef6783a05f0b3f885591e7d2c7b2aec1a89dea-refs/branch-heads/6533@{#1999}) on port 18378
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
8月 26, 2024 5:58:26 午前 org.openqa.selenium.remote.ProtocolHandshake createSession
情報: Detected dialect: W3C
2024-08-26 05:58:26.224 INFO  c.k.k.core.webui.driver.DriverFactory    - sessionId = 68e0165acd3db7bee4c887039897bea0
2024-08-26 05:58:26.273 INFO  c.k.k.core.webui.driver.DriverFactory    - browser = Chrome 127.0.0.0
2024-08-26 05:58:26.293 INFO  c.k.k.core.webui.driver.DriverFactory    - platform = Mac OS X
2024-08-26 05:58:26.315 INFO  c.k.k.core.webui.driver.DriverFactory    - seleniumVersion = 3.141.59
2024-08-26 05:58:26.338 INFO  c.k.k.core.webui.driver.DriverFactory    - proxyInformation = ProxyInformation { proxyOption=NO_PROXY, proxyServerType=HTTP, username=, password=********, proxyServerAddress=, proxyServerPort=0, executionList="", isApplyToDesiredCapabilities=true }
2024-08-26 05:58:26.399 DEBUG testcase.TC1                             - 4: maximizeWindow()
2024-08-26 05:58:26.833 DEBUG testcase.TC1                             - 5: navigateToUrl(url)
2024-08-26 05:58:28.484 DEBUG testcase.TC1                             - 6: toIframe = TestObjectFactory.makeTestObjectXPath("toIframe", "//div[contains(@class,'sandpack') and contains(.,'App.tsx')][5]//iframe")
2024-08-26 05:58:28.543 DEBUG testcase.TC1                             - 7: verifyElementPresent(toIframe, 10)
2024-08-26 05:58:31.998 DEBUG testcase.TC1                             - 8: switchToDefaultContent()
2024-08-26 05:58:32.139 DEBUG testcase.TC1                             - 9: b = switchToFrame(toIframe, 10, STOP_ON_FAILURE)
2024-08-26 05:58:32.555 DEBUG testcase.TC1                             - 10: assert b : "failed to switch to the iframe"
2024-08-26 05:58:32.564 DEBUG testcase.TC1                             - 11: toRoot = TestObjectFactory.makeTestObjectXPath("toRoot", "/html/body")
2024-08-26 05:58:32.590 DEBUG testcase.TC1                             - 12: verifyElementPresent(toRoot, 10, STOP_ON_FAILURE)
2024-08-26 05:58:33.192 DEBUG testcase.TC1                             - 13: toInput = TestObjectFactory.makeTestObjectXPath("toInput", "//*[@id='root']/input")
2024-08-26 05:58:33.208 DEBUG testcase.TC1                             - 14: verifyElementPresent(toInput, 10, STOP_ON_FAILURE)
2024-08-26 05:58:34.575 DEBUG testcase.TC1                             - 15: delay(3)
2024-08-26 05:58:37.656 DEBUG testcase.TC1                             - 16: clearText(toInput)
2024-08-26 05:58:37.823 DEBUG testcase.TC1                             - 17: sendKeys(toInput, "Hooah")
2024-08-26 05:58:38.602 DEBUG testcase.TC1                             - 18: delay(3)
2024-08-26 05:58:41.628 DEBUG testcase.TC1                             - 19: toValue = TestObjectFactory.makeTestObjectXPath("toValue", "//*[@id='root']/p")
2024-08-26 05:58:41.647 DEBUG testcase.TC1                             - 20: verifyElementText(toValue, "Value: Hooah")
2024-08-26 05:58:41.732 DEBUG testcase.TC1                             - 21: switchToDefaultContent()
2024-08-26 05:58:41.753 DEBUG testcase.TC1                             - 22: closeBrowser()
2024-08-26 05:58:42.085 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

The improved Test Case will run faster than the original because it has less amount of processing (emitting messages) to do.

1 Like

Please note that custom “Keywords” is an Enterprise feature as of v9.1.0 of Katalon Studio. You are not allowed to make a custom Groovy class in the recent Free versions. If you want to stick to the Free one and want to create custom Keywords, you need to downgradle to v9.0.0 and stay with it.

1 Like

Thank you,

I’ll try the text file idea seems easier for me to have a file for them instead of having them all in script if I need to edit some.