Katalon has improved the HTML reporting to a new format. In this new format step description is not displayed. How do we add step’s description into new HTML report?
To display step descriptions in Katalon Studio’s new HTML reports, use one of the following approaches:
1. Use @Description Annotation for Custom Keywords
Annotate custom keywords with @Description to include step details in reports:
groovy
import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.annotation.Description
@Description("Step Description: Log into the application")
def login(String username, String password) {
WebUI.setText(findTestObject('Username_Field'), username)
WebUI.setEncryptedText(findTestObject('Password_Field'), password)
WebUI.click(findTestObject('Login_Button'))
}
The description will appear in the Log section of the HTML report.
2. Add Descriptions via KeywordUtil
Explicitly log step descriptions using KeywordUtil:
groovy
import com.kms.katalon.core.util.KeywordUtil
// For manual steps
KeywordUtil.logInfo("Step Description: Verify user dashboard loads")
WebUI.verifyElementPresent(findTestObject('Dashboard_Header'), 10)
// For programmatic steps
KeywordUtil.markPassed("Step Description: File uploaded successfully")
3. Configure testListeners to Auto-Log Steps
Create a custom test listener to inject step descriptions into reports:
- Create a Listener Class:
groovy
// File: Keywords/listeners/StepDescriptionListener.groovy
import com.kms.katalon.core.annotation.AfterTestStep
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.util.KeywordUtil
class StepDescriptionListener {
@AfterTestStep
def afterTestStep(def testCaseContext, def testStepContext) {
String stepDescription = testStepContext.getStepDescription()
if (stepDescription) {
KeywordUtil.logInfo("STEP DESCRIPTION: " + stepDescription)
}
}
}
- Add the Listener to Your Test Case:
groovy
import com.kms.katalon.core.context.TestCaseContext
import com.kms.katalon.core.testcase.TestCaseBinding
TestCaseContext testCaseContext = (TestCaseContext) binding.getVariable("testCaseContext")
testCaseContext.setTestListener(new StepDescriptionListener())
4. Modify Report Templates (Advanced)
Customize Katalon’s report template to include step descriptions:
- Locate the Report Template:
- Default location:
Katalon Studio/plugins/org.katalon.kstudio.reporting_<version>/templates/html.
- Edit
report.html:
Add logic to displaystepDescriptionin the HTML template:
xml
<!-- Insert near step entries -->
<div class="step-description">${step.description}</div>
- Rebuild Reports:
UserebuildReportto apply changes.
5. Use WebUI.comment for Manual Steps
For manual test cases, add descriptions via WebUI.comment:
groovy
WebUI.comment("Step 1: Navigate to the homepage")
WebUI.navigateToUrl("https://example.com")
Troubleshooting Missing Descriptions
- For BDD: Ensure
Scenariosteps include descriptions in.featurefiles:
text
Scenario: User Login
Given I am on the login page # Description: Navigate to login URL
When I enter valid credentials # Description: Input username/password
Then I should see the dashboard # Description: Verify dashboard loads
- For Script Mode: Avoid using
WebUI.failureHandlingflags that skip logging.
By combining these methods, you can ensure step descriptions appear in Katalon’s HTML reports.
Thanks @dineshh for different approaches with so much details.
Approach 2,3,5: actually just adding one more log step before the actual step which is not I wanted todo.
Old html report picks the testStepContext description and set it automatically to the report. For new report it is not working and seems like we will need to wait for futher improvements from Katalon team.
Approach 4: Trying to customize the report is not the best option and not easy to do
Approach 1: It is for custom keywords, I need also for manual steps.
In conclusion: I will switch back to use old html report because the steps are already there and parallel tests execution reports are linked to Collection report from where I can easily navigate through multiple reports which can’t be done yet in new HTML report
Thanks!
I am negative about your idea.
At least, a post in this user forum is not significant enough to ask Katalon to change their product. You should submit an official support request as described at:
I doubt this:
I have never seen a Groovy class com.kms.katalon.core.annotation.AfterTestStep. I checked the source code of Katalon Studio v10.2.0 to see if this class is really there or not.
I found no AfterTestStep class there. This answer looks plausible, but actually it is a fiction. I believe that @dineshh posted this fictious answer without actually verifying it hands-on.
I wonder how @dinessh authored this answer.
Maybe, some AI agent replied this fictitious answer in the name of @dineshh. — AI never verifies its answer.
Are you a human? or an AI agent?
Great, thanks @kazurayam for the link. I will create a request ticket there.
Thank you, I acknowledged this for the first time.
Perhaps, the Katalon programmer who implemented the new HTML report was not aware of the step descriptions in the old HTML report (just like I didn’t know it) so that he/she forgot porting it into the new HTML report. You should escalate this issue.
Good catch, we’ll fix this in the next release, 10.2.1, scheduled for June.
CC: @xuan.tran
Regards;
Philip
Once I wrote:
I was wrong.
I looked into the source code Katalon Studio v10.2.0 and found the com.kms.katalon.core.ast.AstTestStepTransformation class:
@CompileStatic
public void visit(BlockStatement blockStatement, Stack<Statement> deferedStatements, int nestedLevel) {
int index = 0;
int statementIndex = 0;
Map<Statement, Integer> indexMap = getIndexMapForBlockStatement(blockStatement);
if (deferedStatements != null) {
Stack<Statement> copyDeferedStatements = (Stack<Statement>) deferedStatements.clone();
while (!copyDeferedStatements.isEmpty()) {
blockStatement.getStatements().add(0, copyDeferedStatements.pop());
index++;
}
}
List<Statement> statementList = blockStatement.getStatements();
Stack<Statement> commentStatementsStack = new Stack<Statement>();
while (index < statementList.size()) {
Statement statement = statementList.get(index);
String comment = getComment(statement);
if (comment != null) {
commentStatementsStack.push(statement);
index++;
continue;
}
String keywordName = getKeywordNameForStatement(statement);
String description = StringUtils.EMPTY;
boolean isStatementDisabledFlag = false;
if (!(statement instanceof BlockStatement)) {
if (!commentStatementsStack.isEmpty()) {
Statement descriptionStatement = commentStatementsStack.pop();
isStatementDisabledFlag |= isStatementDisabled(descriptionStatement);
String commentContent = description = getComment(descriptionStatement);
blockStatement.getStatements().add(index, new ExpressionStatement(createNewAddDescriptionMethodCall(commentContent)));
index += (popCommentStatements(commentStatementsStack, blockStatement, index, indexMap, nestedLevel) + 1);
}
def keywordInfo = [index, description, keywordName]
List<Statement> tempStatementList = new ArrayList<>();
tempStatementList.add(createBeforeTestStepMethodCall(keywordInfo))
tempStatementList.add(new ExpressionStatement(createNewStartKeywordMethodCall(keywordName, statement, indexMap, nestedLevel)));
tempStatementList.add(createAfterTestStepMethodCall(keywordInfo))
blockStatement.getStatements().addAll(index, tempStatementList);
index += 3;
}
isStatementDisabledFlag |= isStatementDisabled(statement);
if (isStatementDisabledFlag) {
statementList.set(statementList.indexOf(statement), createNewNotRunLogMethodCallStatement(keywordName));
} else {
visit(statement, new Stack<>(), indexMap, nestedLevel + 1);
}
index++;
}
}
This method is interesting. Especially, the following portion does a great trick.
if (!(statement instanceof BlockStatement)) {
if (!commentStatementsStack.isEmpty()) {
Statement descriptionStatement = commentStatementsStack.pop();
isStatementDisabledFlag |= isStatementDisabled(descriptionStatement);
String commentContent = description = getComment(descriptionStatement);
blockStatement.getStatements().add(index, new ExpressionStatement(createNewAddDescriptionMethodCall(commentContent)));
index += (popCommentStatements(commentStatementsStack, blockStatement, index, indexMap, nestedLevel) + 1);
}
This portion transforms a Groovy string literal 'Login' as “description” into the log of the following-sibling statement CustomKeywords.'com.milwaukee.portal.utils.AuthUtils.loginBy'.
![]()
I could not imagine that Katalon would implement such an exaggeration; but it really does. I was surprised.
The code is 10 year olds ![]()
Now is late 12/2025 and there no any fix about the steps description in v10.4.2. I’m very disappointed with the new report. I wonder if Katalon dev ever read the their new test report. It so ugly and unable to understand for BA/PO when there’s no Description.
Pls fix this as soon as posible.
I noticed a difference between the old report and the new report in the way how they present the Test Step Description.
The old report shows the Test Step Description, if there is any, before the “Groovy code fragment” data. The descriptions of all statements are visible initially on page load. Product Owners and Business Analysts can read all the description texts without any click actions on the page.
On the other hand, the new report shows the Test Step Description after the “Groovy code fragment”. The description is initially hidden in an accordion. To make the Description visible, human reader has to click the “TEST STEP: Groovy code fragment” part to open the accordion.
This UI difference indicates that Katalon placed a lower profile on the description data than on the code statement for each test step. They designed it as such.
Is this difference siginificant for you?
Personally, I would prefer the way of old report. And also I would like the fixed string “Description:” to be erased:

Repeating the string “Description:” is just verbose.
Or, the “Description:” string could be replaced with "// ".
TEST STEP: // click the button of Make Appointment
click(findTestObject("Page_CuraHomepage/btn_MakeAppointment"))
This looks concise and more intuitive.
I’m preferring the old way of text presentation more, as it is much more readable and looks like an feature file than a new style
he is a human using AI






