I made a demo project on GitHub:
Problem to solve
In a TestListener with the follwing code,
class MyTestListener {
@BeforeTestCase
def beforeTestCase(TestCaseContext testCaseContext) {
GlobalVariable.CURRENT_TESTCASE_ID = testCaseContext.getTestCaseId()
}
}
GlobalVariable.CURRENT_TESTCASE_ID will have the name of testcase invoked by Katalon Studio.
That’s fine. But we want to know more. Here we assume we have a chain like
- Test Suite
TS1invokes a Test CaseRoot. Rootcalls another Test CaseLevel1, which callsLevel2, which callsLevel3with the built-in keywordWebUI.callTestCase()
I ran the “Test Cases/Root” and looked at the log to find the following result.
Please find that the value of GlobalVariable.CURRENT_TESTCASE_ID stayed the same "Test Cases/Root" inside Level1, Level2 and Leve3. This experiment shows a fact that Katalon Studio does NOT invoke the @beforeTestCase-annotated method in a TestListener when the callTestCase() keyword is called by a parent TestCase. This behavior was a bit of my surprise. But this is given. OK, I would accept it.
However, in some situation due to some reason, I would like to know more:
-
How the code of
"Test Cases/Level1"can find the name of itself is"Test Cases/Level1"? -
How the code of
"Test Cases/Level2"can find the name of itself is"Test Cases/Level2"? -
How the code of
"Test Cases/Level3"can find the name of itself is"Test Cases/Level3"? -
How the code of
"Test Cases/Level1"can find the name of parent:Test Cases/Root? -
How the code of
"Test Cases/Level2"can find the name of parent:Test Cases/Level1? -
How the code of
"Test Cases/Level3"can find the name of parent:Test Cases/Level2?
Solution
Katalon Studio does not provide any built-in solution for the above question.
If you really want the solution, you need to change the source of the built-in keyword WebUI.callTestCase() .
But how the change would be?
I can show you how to change the source of the built-in keyword WebUI.callTestCase() with a running example by changing the implementation runtime using Groovy’s metaprogramming.
Description
Running the demo
- Get the zip file of the demo project from the Releases page, unzip it, open it with your Katalon Studio.
- Open the test suite
TS1, and manually run it. TS1invokes a parent test caseRoot.Rootcalls other test caseLevel1, which callsLevel2, which callsLevel3. The builtin keywordWebUI.callTestCase()is used.- In the Console tab, you can see the following output:
----------------------------------------------------
top-level TestCaseId : Test Cases/Root
----------------------------------------------------
top-level TestCaseId : Test Cases/Root
callee TestCaseId : Test Cases/Level1
parent caller was : Test Cases/Root
----------------------------------------------------
top-level TestCaseId : Test Cases/Root
callee TestCaseId : Test Cases/Level2
parent caller was : Test Cases/Level1
----------------------------------------------------
top-level TestCaseId : Test Cases/Root
callee TestCaseId : Test Cases/Level3
parent caller was : Test Cases/Level2
Notable points
In the Console message, you can find that:
GlobalVariable.CURRENT_TESTCASE_IDstays unchanged during the course of testcase chain. This proves that the TestListener#beforeTestCase() is NOT invoked for the test casesLevel1,Level2andLevel3which are invoked byWebUI.callTestCase()by the caller test cases.- By my trick, you can see the callee TestCaseIds are printed.
My trick
My trick is here in the source code of [ Test Listeners/MyTestListener ](Test Listeners/MyTestListener.groovy).
In the method annotated with @BeforeTestCase , I do
@BeforeTestCase
def beforeTestCase(TestCaseContext testCaseContext) {
// save the name of top-level test case
GlobalVariable.CURRENT_TESTCASE_ID = testCaseContext.getTestCaseId()
// initialize TESTCASE_STACK with java.util.Stack object
if (GlobalVariable.TESTCASES_STACK == null) {
GlobalVariable.TESTCASES_STACK = new Stack<String>()
}
// modify WebUI.callTestCase() implementation
// see for the original https://github.com/katalon-studio/katalon-studio-testing-framework/blob/master/Include/scripts/groovy/com/kms/katalon/core/keyword/BuiltinKeywords.groovy
BuiltinKeywords.metaClass.'static'.callTestCase = { TestCase calledTestCase, Map binding, FailureHandling flowControl ->
((Stack)GlobalVariable.TESTCASES_STACK).push(calledTestCase.getTestCaseId())
Object result = (Object)KeywordExecutor.executeKeywordForPlatform(KeywordExecutor.PLATFORM_BUILT_IN, "callTestCase", calledTestCase, binding, flowControl)
((Stack)GlobalVariable.TESTCASES_STACK).pop()
return result
}
BuiltinKeywords.metaClass.'static'.callTestCase = { TestCase calledTestCase, Map binding ->
((Stack)GlobalVariable.TESTCASES_STACK).push(calledTestCase.getTestCaseId())
Object result = (Object)KeywordExecutor.executeKeywordForPlatform(KeywordExecutor.PLATFORM_BUILT_IN, "callTestCase", calledTestCase, binding)
((Stack)GlobalVariable.TESTCASES_STACK).pop()
return result
}
}
Here I used Groovy’s metaprogromming feature. See ExpandoMetaClass for technical detail. I would not talk about it here.
Please note that the GlobalVariable.TESTCASE_STACK must be declared with type Null in order to initialize it with a Stack object.
