Is there a way to override findTestObject method

I am looking for options to override, findTestObject of the com.kms.katalon.core.testobject.ObjectRepository class. We are looking to include an additional folder to the test objects ID based on a flag. If the execution is for integration testing, then the additional folder should be included, if not we can process it as is.

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!

Hi @koushik.kannan, this might work for you:

To override or customize the findTestObject method in Katalon, you could create a custom keyword. Here’s a basic example to get you started:

  1. Create a Custom Keyword:

    • In Katalon Studio, go to Keywords and create a new package and class.
    • Add the necessary imports, including the static import for findTestObject.
  2. Define Your Custom Method:

    • Write your custom method within the class. Here’s an example:
package customKeywords

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

public class CustomFindTestObject {

    @Keyword
    public TestObject findCustomTestObject(String objectPath, String customXPath) {
        TestObject testObject = findTestObject(objectPath)
        testObject.setSelectorValue(SelectorMethod.XPATH, customXPath)
        return testObject
    }
}

  1. Use Your Custom Keyword:
    • In your test case, call the custom keyword like this:
import customKeywords.CustomFindTestObject as CustomFind

TestObject myObject = CustomFind.findCustomTestObject('Object Repository/MyObject', '//div[@id="customId"]')

This approach would allow you to modify the selector of a test object dynamically.

In order to “override” the findTestObject method, you would need to add another parameter or change one of the “current” parameters so the method profile is different from the current ones that exist. I don’t think you should “override” the findTestObject method (take a look at its code for my reason), but instead, create your own method name, like “findIdObject”.

However, is the reason for your thoughts to override the findTestObject method just to have another directory in the OR. You can create multiple directories in the OR and then you can perhaps copy, paste your current code into your new one with a global rename. ??

If you go with creating your objects in-code like @Dave_Evers shows, then you can make this method “static” also.

Why do you want to do this?

Let me tell you my guess.

@koushik.kannan has a Katalon project “Project A” and another project “Project X”. The ProjextX already has a set of useful testobjects. Now he wants to reuse the “ProjectX/Object Repository” in the Project A.

Am I right?

The “findTestObject” method is implemented in com.kms.katalon.core.testobject.ObjectRepository class. If the ObjectRepository is written in Groovy, then you should have a chance to override a method by Groovy’s Metaprogramming technique.

You can find and read the source code of this class at

<Katalon Studio installation folder on your PC>/Contents/Eclipse/configuration/resources/source/com.kms.katalon.core/com.kms.katalon.core-source.jar

If you read the source, you would find that ObjectRepository is written in Java, not in Groovy. Therefore it is impossible for you to modify the findTestObject method of ObjectRepository class runtime using Groovy’s feature.

However, if you are skilled enough for Java programming, you would be able to read the source of ObjectRepository class and will find it is rather simple. It locates a XML file in a folder, read it to instanciate a TestObject, return the object. That’s all.

Then you would be able to make your own custom Groovy class (so called “Keyword”) that mimics the ObjectRepository class. You should be able to design your custom class so that it can read files from mulitiple folders as Katalon projects given as parameter.

Please be aware that custom “Keyword” requires Enterprises license since v9.1.x. You can not create custom Groovy classes since v9.1.x in the Free plan.

You can manually copy the “Object Repository” folder of the ProjectX into the ProjectA.

The copied TestObjects will just work.

I know, it is a terrible idea to do copy&paste TestObjects across the boundary of projects; nobody can maintain it long term.

Alternative (terrible) idea.

Katalon Studio is designed to have only one project in its scope. It can not support multiple projects in its hand. You should embrace this design.

So, @koushik.kannan wants to merge the 2 projects, ProjectX and ProjectA, into a single Katalon project. He can create subfolders under ObjectRepository where he can create 2 subfolders: X and A. Inside these subfolders, he can locate the test objects migrated from the seperated projects. He can do the same for Test Cases and Test Suites.

Or, you can create a git repository which contains the folders of ProjectX and ProjectA together. Thus you should be able to keep the version of 2 projects loosely in sync. This would be much safer than having 2 git repositories for X and A isolated.

Thanks a lot @Dave_Evers for the quick response. I was more looking at options to use the same method name findTestObject. It should be something like instead of referring to com.kms.katalon.core.testobject.ObjectRepository.findTestObject, it should refer to the overridden findTestObject in any of the custom class i created, there by i should be able to replace

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

with

import static customClass.findTestObject

1 Like

@kazurayam, I wouldnt want to merge the code by which we need to maintain 2 different redundant versions of code independently, rather use the 4 project repos only for execution purposes, so that we can maintain them only once.

Thanks a lot @kazurayam and @grylion54 for the wonderful suggestions. As @kazurayam pointed out, i have 4 projects and 1 base project, i have to refer to the objects, scripts, datafiles and utilities in these 4 projects from my base project. Right now, the approach we have taken is to selectively copy the necessary files from these 4 repos and create a folder in the respective base project and paste them. Since we are introducing one more layer of folder structure, we want to override the findTestObject without impacting the existing code.

Right now, it looks like there is no way to leave the copied code untouched, either create a custom method and find & replace findTestObject with Helper.findTestObject or extend ObjectRepository, Override the findTestObject method and replace
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject

with

import static Helper.findTestObject

OK. I understand it.

One more idea.

If I were you, I would use Gradle to copy files/directories from the base project into the project for execution purpose. — I like Gradle, I am expericed enough with it.

If I have ProjectA, ProjectB, ProjectC and ProjectD which depends on the base ProjctX. Then I would write “build.gradle” file in each ProjectA, B, C, D.

The build.gradle files will implement “importProjectX” task. The task will implement all necessary stuffs to import the test objects cleanly. The build.gradle would be/could be complex. I can do any tricks in it.

When I update the base ProjectX, then I would run “importProjectX” in each 4 acting projects.

So, what about creating a method like below to adjust the pathway to your new OR folder.

public TestObject findHelperObject(String pathway) {
	if (pathway.startsWith("Object Repository/")) {
		pathway = pathway.replace("Object Repository/", "")
	}
	String result = "newPath/" + pathway
	return findTestObject(result)
}

"and you use like"
WebUI.click(findHelperObject('Object Repository/myPage/a_Administrator'))

I have implemented this idea, created a sample, published at GitHub. See the following post:

Thank you very much for all the insightful solutions. For now, we have proceeded with creating a findTestObject method in a Helper class that extends ObjectRepository. Now we are faced with another challenge, in the 4 projects the object repository values are repeating and we need to deduce which call to a particular object is from which folder that we have copied. For example, loginbutton is present in folder ABC, DEF in project A, GHI, DEF in project B, XYZ in project C and PQR in project D. If the call is for login button in folder DEF, i want to deduce at run time if it belongs to Project A or project B. If i can get the path of the step definition file which calls this findTestObject method, i can extract information on the project since the stepdefs are maintained in the project name hierarchy.

I do not quite understand your issue above. I need more concrete infomration. Still I have some words to tell you.

The ProjectA/build.gradle and the ProjectB/build.gradle — these are almost similar but you can make them different in detail.

Let me show you how these 2 files could be different in detail.

In ProjectA/build.gradle, you could write

  doLast {
    ...
    copy {
      from("$USER_HOME/$BASE_PROJECT_SUBPATH/Object Repository/Page_CURA Healthcare Service") {
        include 'ABC/**/*.rs'
        include 'DEF/**/*.rs'
      }
    ...

In the ProjectB/build.gradle,

  doLast {
    ...
    copy {
      from("$USER_HOME/$BASE_PROJECT_SUBPATH/Object Repository/Page_CURA Healthcare Service") {
        include 'GHI/**/*.rs'
        include 'DEF/**/*.rs'
      }
    ...

Also, Gradle’s Copy task supports renaming files/folders.

https://docs.gradle.org/current/userguide/working_with_files.html#renaming_files

Using the rename statement, we can make

TestObject name in the Base copy&rename TestObject name in the ProjectA
ABC → GHI
ABC → LMN

You can customize the ProjectA/build.gradle and the ProjectB/build.gradle as much as you need. These are 2 independent text files.

I suppose you would be able to find a clue to your issue in the Gradle, “Working with files” document. For example, the

Gradle Copy task enables you to “filter” the file content while copying it.

https://docs.gradle.org/current/userguide/working_with_files.html#sec:filtering_files

Filtering file content in Gradle involves replacing placeholders or tokens in files with dynamic values.

However, if you find the idea of filtering the locators on copy inteteresting, possibly you should consider changing the original TestObjects in the Base project so that it employs the “Parameterized Test Object” format.

Provided with a parameterized locator (XPath , CSS Selector) like

  • "//div[contains(@class,'${PLACEHOLDER}')]/div[@id='login-button']"

The caller Test Cases in each projects can supply the value to the PLACEHOLDER runtime. The locator will be compleded, for example, to be

  • "//div[contains(@class,'ABC')]/div[@id='login-button']"

runtime in the ProjectA.

Possibly this is what @koushik.kannan wants.


Another variation. Gradle copy task is capable to “filter” the parameterized locator with value specified in the build.gradle, so that I can do transformation

In BaseProject build.gradle in ProjectA
//div[contains(@class,‘${PLACEHOLDER}’)]/div[@id=‘login-button’] PLACEHOLDER = “ABC” //div[contains(@class,‘ABC’)]/div[@id=‘login-button’]

Thank you @kazurayam for the solutions. But we are not using gradle here and dont have plans to use them as well. Anyways, I am new getting new requirements day by day and would want to ask so many questions one after the other since the situation is evolving. Will consolidate and ask the question if required in one go.

Thanks everyone for taking time in responding to my query and providing wonderful solutions. I learnt a lot from these discussions and hoping to connect back with another query in the future.

1 Like