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 displaystepDescription
in the HTML template:
xml
<!-- Insert near step entries -->
<div class="step-description">${step.description}</div>
- Rebuild Reports:
UserebuildReport
to 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
Scenario
steps include descriptions in.feature
files:
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.failureHandling
flags 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 believe that the “step description” was not displayed even in the old format of HTML report.
A “step description” is serialized as a string literal in Test Case script.
Even in the new report, it would be impossible.
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?
Old HTML report does display step description, it really helps to read and understand the steps for business users or for any person who interested in the tests.
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