Configuring a Katalon Studio project with JSON files

I have published a GitHub repository

Problem to solve

Katalon Studio provides Execution Profile and Global Variables.

Execution Profile helps cover multiple and different environments to execute your automation test scripts with ease. You can configure the testing environment in terms of data and behaviors through Global variables.

You can update the value of GlobalVariable in memory during test runs but you can not persist the updates into Execution Profiles. If you want to alter the values of Execution Profiles, editing the values in Katalon Studio’s GUI is the only way.

I am not satisfied with this constraint. I want to find a way to update the config information programmatically and persist it into disk during a Test Suite run; then later I want to reuse the updated config information in another Test Suite run. How can achieve it?

Solution

  • In the project folder, I will add JSON text files as a container of config information. Thanks to the expressiveness of JSON, I can express anything.

  • I will create a GlobalVariable of type Null.

  • My test script will explicitly load a JSON configuration file, turn it into an Object, substitute it into a GlobalVariable. The GlobalVariable will be shared by all Test Cases in a Test Suite. My test script will update the Object during test run, and will explicitly save it into a JSON file.

My test script will fully utilize the Groovy language’s features of processing JSON. Thanks to the Groovy’s features of processing JSON, it is quite easy and straight-forward to implement this processing. I will show you a full set of sample code. Please note that this approach is intended for seasoned programmers. I’m afraid, non-programmers can not enjoy the power of my approach.

Description of the demo implementation

JSON configuration file

I created a text file in JSON format: <projectDir>/ksconfig.json

{
	"bill": "debit",
	"home": "debit",
	"money": "credit"
}

GlobalVariable.config

I created a GlobalVariable named config in the default Execution Profile. I assigned the type Null to it.

This GlobalVariable is used as a placeholder for an Object deserialized from JSON file. The GlobalVariable will be visible for all Test Cases in a Test Suite.

Groovy class that wraps reading/writing JSON

Keywords/com/kazurayam/ks/ConfigIO.groovy implements the read method and the wrtie method for JSON file. The TC1 and TC4 use this Groovy class to read / write JSON files.

package com.kazurayam.ks

import groovy.json.JsonOutput
import groovy.json.JsonSlurper

public class ConfigIO {

	public static Object read(File jsonFile) {
		assert jsonFile.exists()
		assert jsonFile.isFile()
		JsonSlurper slurper = new JsonSlurper()
		def config = slurper.parse(jsonFile)
		return config
	}

	public static void write(Object config, File jsonFile) {
		assert config != null
		ensureParentDirectory(jsonFile)
		jsonFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(config))
	}

	private static void ensureParentDirectory(File file) {
		File parent = file.getParentFile()
		if (!parent.exists()) {
			boolean b = parent.mkdirs()
			if (!b) throw new IOException("failed to create " + parent.toString())
		}
	}
}

Test Case TC1 — load the JSON to GlobalVariable

Test Cases/TC1 loads a JSON file, deserialize it into an Object, put the Object into the placeholder GlobalVariable.

import com.kazurayam.ks.ConfigIO

import groovy.json.JsonOutput
import internal.GlobalVariable

/*
 * load the config file and put it into a GlobalVariable to share
 * in the scope of a Test Suite
 */
GlobalVariable.config = ConfigIO.read(new File('./ksconfig.json'))

println "[TC1] " + JsonOutput.prettyPrint(JsonOutput.toJson(GlobalVariable.config))

Test Case TC2 — update information programmatically

Test Cases/TC2 updates the Object programmatically.

import internal.GlobalVariable as GlobalVariable

/*
 * demonstrate that we can update the GlobalVariable in memory
 */
println "GlobalVariable.config before update: " + GlobalVariable.config

GlobalVariable.config['home'] = 'mona lisa'

println "GlobalVariable.confing after update: " + GlobalVariable.config

Test Case TC3 — demonstrate that the updated information is carried over

Test Cases/TC3 demostrates that the Object updated by the preceding TC2 is carried over and visible for TC3.

import internal.GlobalVariable as GlobalVariable

/*
 * demostrate that the GlobalVariable which was updated by the preceding Test Case TC2
 * is globaly shared in the Test Suite scope
 */
println "GlobalVariable.confing carried over: " + GlobalVariable.config

Test Case TC4 — persist the information into an external file

Test Cases/TC4 serializes the updated Object into JSON and save it into a text file.

import com.kazurayam.ks.ConfigIO

import groovy.json.JsonOutput
import internal.GlobalVariable as GlobalVariable

/*
 * demonstrate that we can save the updated config into an external JSON file
 */
GlobalVariable.config['money'] = 12345

// save the config into a file
// you can specify the original file name 'ksconfig.json' to overwrite it if you want to
File f2 = new File('./ksconfig2.json')
ConfigIO.write(GlobalVariable.config, f2)

// look into the saved text
println "[TC4] " + f2.text

Please pay attention to the output file name.
If you specify a new name, then a new file will be created.
If you specify the name of source JSON file, then the file will be overwritten.
Which way to take?
It is up to you.

Test Suite TS1 — run TC1 + TC2 + TC3 + TC4

I made a Test Suite TS1, which just runs TC1, TC2, TC3 and TC4 in this sequence.
When I ran it, I got the following output in the console:

...
GlobalVariable.config before update: [bill:debit, home:debit, money:credit]
...
GlobalVariable.confing after update: [bill:debit, home:mona lisa, money:credit]
...
GlobalVariable.confing carried over: [bill:debit, home:mona lisa, money:credit]
...
{
    "bill": "debit",
    "home": "mona lisa",
    "money": 12345
}
...

Test Suite Collection — run TS1 and TS2 where the ksconfig2.json file is passed

I added a Test Suite (TS2) and a Test Suite Collection (TSC0). The TSC0 binds the TS1 and the TS2 sequentially.

When I run the TSC0,
The TS1 printed this:

[TC1] {
    "bill": "debit",
    "home": "debit",
    "money": "credit"
}
[TC2] GlobalVariable.config before update: [bill:debit, home:debit, money:credit]
[TC2] GlobalVariable.confing after update: [bill:debit, home:mona lisa, money:credit]
[TC3] GlobalVariable.confing carried over: [bill:debit, home:mona lisa, money:credit]
[TC4] {
    "bill": "debit",
    "home": "mona lisa",
    "money": 12345
}

And the TS2 printed this:

[TC9] {
    "bill": "debit",
    "home": "mona lisa",
    "money": 12345
}
[TC9] bill: debit
[TC9] home: mona lisa
[TC9] money: 12345

The output clearly shows that the TS2 (precisely TC9) could read the ksconfig2.json file which was updated by the preceding TS1.

Conclusion

I think that the built-in features in Katalon Studio GUI around GlobalVariable are designed with an assumption that users will be happy and satisfied with manually edited Execution Profiles (= GlobalVariables); they would never want to update Execution Profiles on disk programmatically during the executions of Test Case scripts. I think that this assumption is appropriate as long as Katalon Studio is designed for non-programmers.

On the other hand, A post in Katalon user forum discussed how to break this design by overwriting XML files as the serialized format of Execution Profiles. I do not think their approach would be successful. I think we shouldn’t try to modify the behavior of Katalon Studio. Just leave it as is. I think, we had better invent an alternative way of configuring a Test Suite by custom Groovy scripting.

A shortage of my approach is that it does not provide any GUI component to edit/view the JSON configuration as a part of Katalon Studio GUI. But I don’t mind it. I don’t need any GUI support. A text editor is saficient for me to handle JSON files.

3 Likes

I would like to propose a usage pattern where we use Execution Profiles and JSON config files in combination. See the following:

I will create 3 Execution Profiles

  • development-env
  • production-env
  • stating-env

All these 3 Execution profiles declares a common GlobalVariable named config. The GlobalVariable.config will be String type, it will contain the path to the configuration JSON file.

I will create 3 JSON files

  • ./config-development.json
  • ./config-production.json
  • ./config-staging.json

In these 3 JSON files, I will declare any configuration information to control the test. For example, the target’s hostname, port, etc.

Of course I will link the Execution Profiles to JSON configs like

  • development-env./config-development.json
  • production-env./config-production.json
  • staging-env./config-staging.json

On the top-right side of the Katalon Studio GUI, you can choose one out of the 3 Execution Profiles development-env, production-env and staging-env.

This means effectively you can choose, by GUI operation, one out of the 3 JSON configuration files to apply to your test execution. Similarly, in Katalon Runtime Engine, you choose it by a command line parameter.

This way makes it possible for me to write all the configuration information of 3 environments in JSON text. I prefer JSON to XML for configuration format. No need to discuss why.

Do you have more environments to test? — Fine. You want to add more pairs of Execution Profile - JSON.

I’m a little disappointed there’s no acknowledgement of prior art.

1 Like

I have updated the demo project.

I added a Test Suite (TS2) and a Test Suite Collection (TSC0). The TSC0 binds the TS1 and the TS2 sequentially.

When I run the TSC0,
The TS1 printed this:

2026-02-14 18:25:14.421 INFO  c.k.k.c.l.logback.LogbackConfigurator    - Logback default configuration initialized from: /Applications/Katalon Studio Enterprise.app/Contents/Eclipse/configuration/org.eclipse.osgi/132/0/.cp/resources/logback/logback-execution.xml
2026-02-14 18:25:14.427 INFO  c.k.k.c.l.logback.LogbackConfigurator    - Logback custom configuration initialized from: /Users/kazuakiurayama/katalon-workspace/ConfiguringKatalonProjectWithJson/Include/config/log.properties
[TC1] {
    "bill": "debit",
    "home": "debit",
    "money": "credit"
}
[TC2] GlobalVariable.config before update: [bill:debit, home:debit, money:credit]
[TC2] GlobalVariable.confing after update: [bill:debit, home:mona lisa, money:credit]
[TC3] GlobalVariable.confing carried over: [bill:debit, home:mona lisa, money:credit]
[TC4] {
    "bill": "debit",
    "home": "mona lisa",
    "money": 12345
}

And the TS2 printed this:

2026-02-14 18:25:24.491 INFO  c.k.k.c.l.logback.LogbackConfigurator    - Logback default configuration initialized from: /Applications/Katalon Studio Enterprise.app/Contents/Eclipse/configuration/org.eclipse.osgi/132/0/.cp/resources/logback/logback-execution.xml
2026-02-14 18:25:24.496 INFO  c.k.k.c.l.logback.LogbackConfigurator    - Logback custom configuration initialized from: /Users/kazuakiurayama/katalon-workspace/ConfiguringKatalonProjectWithJson/Include/config/log.properties
[TC9] {
    "bill": "debit",
    "home": "mona lisa",
    "money": 12345
}
[TC9] bill: debit
[TC9] home: mona lisa
[TC9] money: 12345

The output clearly shows that the TS2 (precisely TC9) could read the myconfig2.json file which was updated by the preceding TS1.

totally agree :100:

thanks so much Mr Kazu
you’ve been a huge help to so many Katalon members

I updated the demo project more. I introduced a Groovy class com.kazurayam.ks.ConfigIO. This class implements read and write method which hides the JSON processing detail. The TC1 and TC4 now use the ConfigIO class.