Skip to content

Commit a6fe13c

Browse files
authored
Merge pull request #110 from testomatio/Issue-103_Karate
Issue-103 Improve step logging for Karate tests
2 parents 06257ae + 455454f commit a6fe13c

17 files changed

Lines changed: 466 additions & 139 deletions

File tree

java-reporter-core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<groupId>io.testomat</groupId>
99
<artifactId>java-reporter-core</artifactId>
10-
<version>0.9.0</version>
10+
<version>0.9.1</version>
1111
<packaging>jar</packaging>
1212

1313
<name>Testomat.io Reporter Core</name>

java-reporter-core/src/main/java/io/testomat/core/constants/CommonConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.testomat.core.constants;
22

33
public class CommonConstants {
4-
public static final String REPORTER_VERSION = "0.8.7";
4+
public static final String REPORTER_VERSION = "0.9.1";
55

66
public static final String TESTS_STRING = "tests";
77
public static final String API_KEY_STRING = "api_key";
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.testomat.core.step;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
6+
public class StepTimer {
7+
8+
private static final Map<String, Long> START_TIMES = new ConcurrentHashMap<>();
9+
10+
public static void start(String key) {
11+
START_TIMES.put(key, System.currentTimeMillis());
12+
}
13+
14+
public static long stop(String key) {
15+
Long start = START_TIMES.remove(key);
16+
if (start == null) {
17+
return -1;
18+
}
19+
return System.currentTimeMillis() - start;
20+
}
21+
}

java-reporter-cucumber/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.testomat</groupId>
88
<artifactId>java-reporter-cucumber</artifactId>
9-
<version>0.7.10</version>
9+
<version>0.7.11</version>
1010
<packaging>jar</packaging>
1111

1212
<name>Testomat.io Java Reporter Cucumber</name>
@@ -51,7 +51,7 @@
5151
<dependency>
5252
<groupId>io.testomat</groupId>
5353
<artifactId>java-reporter-core</artifactId>
54-
<version>0.9.0</version>
54+
<version>0.9.1</version>
5555
</dependency>
5656
<dependency>
5757
<groupId>org.slf4j</groupId>

java-reporter-junit/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.testomat</groupId>
88
<artifactId>java-reporter-junit</artifactId>
9-
<version>0.8.1</version>
9+
<version>0.8.2</version>
1010
<packaging>jar</packaging>
1111

1212
<name>Testomat.io Java Reporter JUnit</name>
@@ -51,7 +51,7 @@
5151
<dependency>
5252
<groupId>io.testomat</groupId>
5353
<artifactId>java-reporter-core</artifactId>
54-
<version>0.9.0</version>
54+
<version>0.9.1</version>
5555
</dependency>
5656
<dependency>
5757
<groupId>org.slf4j</groupId>

java-reporter-karate/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.testomat</groupId>
88
<artifactId>java-reporter-karate</artifactId>
9-
<version>0.2.2</version>
9+
<version>0.2.3</version>
1010
<packaging>jar</packaging>
1111

1212
<name>Testomat.io Java Reporter Karate</name>
@@ -52,7 +52,7 @@
5252
<dependency>
5353
<groupId>io.testomat</groupId>
5454
<artifactId>java-reporter-core</artifactId>
55-
<version>0.9.0</version>
55+
<version>0.9.1</version>
5656
</dependency>
5757
<dependency>
5858
<groupId>io.karatelabs</groupId>
@@ -117,7 +117,7 @@
117117
<artifactId>maven-javadoc-plugin</artifactId>
118118
<version>${maven-javadoc-plugin.version}</version>
119119
<configuration>
120-
<source>11</source>
120+
<source>17</source>
121121
<quiet>true</quiet>
122122
<doclint>none</doclint>
123123
</configuration>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.testomat.karate.adapter;
2+
3+
import com.intuit.karate.core.ScenarioEngine;
4+
import com.intuit.karate.core.Variable;
5+
6+
public class CustomKarateEngineAdapter implements KarateEngineAdapter {
7+
8+
@Override
9+
public Object getVariable(String name) {
10+
ScenarioEngine engine = ScenarioEngine.get();
11+
Variable variable = engine.vars.get(name);
12+
return variable != null ? variable.getValue() : null;
13+
}
14+
15+
@Override
16+
public void setVariable(String name, Object value) {
17+
ScenarioEngine.get().setVariable(name, value);
18+
}
19+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.testomat.karate.adapter;
2+
3+
public interface KarateEngineAdapter {
4+
5+
Object getVariable(String name);
6+
7+
void setVariable(String name, Object value);
8+
}

java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import static io.testomat.core.constants.CommonConstants.FAILED;
44
import static io.testomat.core.constants.CommonConstants.PASSED;
55
import static io.testomat.core.constants.CommonConstants.SKIPPED;
6-
import static java.util.Objects.isNull;
76

87
import com.intuit.karate.core.ScenarioResult;
98
import com.intuit.karate.core.ScenarioRuntime;
@@ -12,9 +11,7 @@
1211
import java.io.PrintWriter;
1312
import java.io.StringWriter;
1413
import java.util.Arrays;
15-
import java.util.Map;
1614
import java.util.Objects;
17-
import java.util.Optional;
1815
import java.util.UUID;
1916
import java.util.stream.Stream;
2017
import org.slf4j.Logger;
@@ -47,47 +44,21 @@ public ExceptionDetails extractExceptionDetails(ScenarioRuntime sr) {
4744
}
4845

4946
/**
50-
* Extracts a test identifier from a Karate test execution context.
47+
* Extracts a test identifier from scenario tags.
5148
* <p>
52-
* The method attempts to resolve the test identifier using the following priority:
53-
* <ol>
54-
* <li>
55-
* Scenario Outline {@code Examples} data — looks for an example column
56-
* whose name matches the configured test id prefix.
57-
* </li>
58-
* <li>
59-
* Scenario tags — looks for tags starting with the same prefix
60-
* (for example, {@code TestId:}).
61-
* </li>
62-
* </ol>
63-
* <p>
64-
* The extracted value is validated against the configured test id format.
65-
* No additional normalization is applied to the returned value.
66-
* <p>
67-
* If no valid test identifier is found in either source, the method returns {@code null}.
49+
* Searches for the first tag that starts with the configured {@code TEST_ID_PREFIX}
50+
* (case-insensitive), removes the prefix, and validates the remaining value.
6851
*
69-
* @param sr the {@link ScenarioRuntime} representing the executed Karate test case
70-
* @return the extracted test identifier, or {@code null} if not found
52+
* @param sr the {@link ScenarioRuntime} of the executed Karate scenario
53+
* @return the extracted test identifier, or {@code null} if none is found
7154
*/
7255
public String extractTestId(ScenarioRuntime sr) {
73-
String testId = findFirstValidTestId(
74-
Optional.ofNullable(sr.scenario.getExampleData())
75-
.stream()
76-
.flatMap(m -> m.entrySet().stream())
77-
.filter(e -> e.getKey().equalsIgnoreCase(TEST_ID_PREFIX))
78-
.map(Map.Entry::getValue)
79-
.map(Object::toString)
80-
);
81-
82-
if (isNull(testId)) {
83-
testId = findFirstValidTestId(sr.tags.getTags().stream()
84-
.filter(Objects::nonNull)
85-
.filter(tag -> tag.regionMatches(true, 0,
86-
TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
87-
.map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));
88-
}
56+
return findFirstValidTestId(sr.tags.getTags().stream()
57+
.filter(Objects::nonNull)
58+
.filter(tag -> tag.regionMatches(true, 0,
59+
TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
60+
.map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));
8961

90-
return testId;
9162
}
9263

9364
/**

java-reporter-karate/src/main/java/io/testomat/karate/hooks/KarateHook.java

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,49 @@
55
import com.intuit.karate.RuntimeHook;
66
import com.intuit.karate.Suite;
77
import com.intuit.karate.core.ScenarioRuntime;
8+
import com.intuit.karate.core.Step;
9+
import com.intuit.karate.core.StepResult;
810
import io.testomat.core.exception.ReportTestResultException;
911
import io.testomat.core.model.TestResult;
1012
import io.testomat.core.runmanager.GlobalRunManager;
13+
import io.testomat.core.step.StepStorage;
14+
import io.testomat.core.step.StepTimer;
15+
import io.testomat.core.step.TestStep;
16+
import io.testomat.karate.adapter.CustomKarateEngineAdapter;
17+
import io.testomat.karate.adapter.KarateEngineAdapter;
1118
import io.testomat.karate.constructor.KarateTestResultConstructor;
1219
import io.testomat.karate.exception.KarateHookException;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
1322

1423
/**
1524
* Runtime hook for integrating Karate test execution with Testomat.io.
16-
* Reports Karate test execution results to the Testomat.io platform.
25+
* Reports Karate test execution results to the
26+
* Testomat.io platform.
1727
*/
1828
public class KarateHook implements RuntimeHook {
1929

30+
private static final Logger log = LoggerFactory.getLogger(KarateHook.class);
31+
32+
private static final String LOG_NEXT_STEP = "log_next_step";
33+
private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";
34+
private static final String LOG_STEPS = "LogSteps";
35+
2036
private final KarateTestResultConstructor resultConstructor;
2137
private final FacadeFunctionsHandler functionsHandler;
2238
private final GlobalRunManager runManager;
39+
private final KarateEngineAdapter engine;
2340

2441
public KarateHook(
2542
KarateTestResultConstructor resultConstructor,
2643
FacadeFunctionsHandler functionsHandler,
27-
GlobalRunManager runManager
44+
GlobalRunManager runManager,
45+
KarateEngineAdapter engine
2846
) {
2947
this.resultConstructor = resultConstructor;
3048
this.functionsHandler = functionsHandler;
3149
this.runManager = runManager;
50+
this.engine = engine;
3251
}
3352

3453
/**
@@ -39,7 +58,8 @@ public KarateHook() {
3958
this(
4059
new KarateTestResultConstructor(),
4160
new FacadeFunctionsHandler(),
42-
GlobalRunManager.getInstance()
61+
GlobalRunManager.getInstance(),
62+
new CustomKarateEngineAdapter()
4363
);
4464
}
4565

@@ -48,6 +68,51 @@ public void beforeSuite(Suite suite) {
4868
runManager.incrementSuiteCounter();
4969
}
5070

71+
@Override
72+
public boolean beforeStep(Step step, ScenarioRuntime sr) {
73+
boolean isMarked = Boolean.TRUE.equals(engine.getVariable(LOG_NEXT_STEP));
74+
75+
boolean isDslStep = switch (step.getPrefix() == null ? "" : step.getPrefix()) {
76+
case "Given", "When", "Then", "And", "But" -> true;
77+
default -> false;
78+
};
79+
80+
boolean logAllSteps = isDslStep && sr.tags.getTags().contains(LOG_STEPS);
81+
82+
if (logAllSteps || isMarked) {
83+
engine.setVariable(LOG_NEXT_STEP, false);
84+
String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
85+
StepTimer.start(stepId);
86+
}
87+
return true;
88+
}
89+
90+
@Override
91+
public void afterStep(StepResult result, ScenarioRuntime sr) {
92+
Step step = result.getStep();
93+
String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
94+
long durationMillis = StepTimer.stop(stepId);
95+
96+
if (durationMillis != -1) {
97+
String stepName = step.getText();
98+
Object title = engine.getVariable(LOG_NEXT_STEP_TITLE);
99+
100+
if (title != null) {
101+
stepName = title.toString();
102+
engine.setVariable(LOG_NEXT_STEP_TITLE, null);
103+
}
104+
105+
TestStep testStep = new TestStep();
106+
testStep.setCategory("user");
107+
testStep.setStepTitle(stepName);
108+
testStep.setDuration(durationMillis);
109+
110+
StepStorage.addStep(testStep);
111+
112+
log.debug("Step '{}' completed in {} ms", stepName, durationMillis);
113+
}
114+
}
115+
51116
@Override
52117
public void afterScenario(ScenarioRuntime sr) {
53118
if (!runManager.isActive()) {

0 commit comments

Comments
 (0)