io.karatelabs
@@ -117,7 +117,7 @@
maven-javadoc-plugin
${maven-javadoc-plugin.version}
- 11
+ 17
true
none
diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/adapter/CustomKarateEngineAdapter.java b/java-reporter-karate/src/main/java/io/testomat/karate/adapter/CustomKarateEngineAdapter.java
new file mode 100644
index 00000000..cc493e7a
--- /dev/null
+++ b/java-reporter-karate/src/main/java/io/testomat/karate/adapter/CustomKarateEngineAdapter.java
@@ -0,0 +1,19 @@
+package io.testomat.karate.adapter;
+
+import com.intuit.karate.core.ScenarioEngine;
+import com.intuit.karate.core.Variable;
+
+public class CustomKarateEngineAdapter implements KarateEngineAdapter {
+
+ @Override
+ public Object getVariable(String name) {
+ ScenarioEngine engine = ScenarioEngine.get();
+ Variable variable = engine.vars.get(name);
+ return variable != null ? variable.getValue() : null;
+ }
+
+ @Override
+ public void setVariable(String name, Object value) {
+ ScenarioEngine.get().setVariable(name, value);
+ }
+}
diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/adapter/KarateEngineAdapter.java b/java-reporter-karate/src/main/java/io/testomat/karate/adapter/KarateEngineAdapter.java
new file mode 100644
index 00000000..b42a1d00
--- /dev/null
+++ b/java-reporter-karate/src/main/java/io/testomat/karate/adapter/KarateEngineAdapter.java
@@ -0,0 +1,8 @@
+package io.testomat.karate.adapter;
+
+public interface KarateEngineAdapter {
+
+ Object getVariable(String name);
+
+ void setVariable(String name, Object value);
+}
diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java b/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java
index 99679ce5..eb114669 100644
--- a/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java
+++ b/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java
@@ -3,7 +3,6 @@
import static io.testomat.core.constants.CommonConstants.FAILED;
import static io.testomat.core.constants.CommonConstants.PASSED;
import static io.testomat.core.constants.CommonConstants.SKIPPED;
-import static java.util.Objects.isNull;
import com.intuit.karate.core.ScenarioResult;
import com.intuit.karate.core.ScenarioRuntime;
@@ -12,9 +11,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
-import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
import org.slf4j.Logger;
@@ -47,47 +44,21 @@ public ExceptionDetails extractExceptionDetails(ScenarioRuntime sr) {
}
/**
- * Extracts a test identifier from a Karate test execution context.
+ * Extracts a test identifier from scenario tags.
*
- * The method attempts to resolve the test identifier using the following priority:
- *
- * -
- * Scenario Outline {@code Examples} data — looks for an example column
- * whose name matches the configured test id prefix.
- *
- * -
- * Scenario tags — looks for tags starting with the same prefix
- * (for example, {@code TestId:}).
- *
- *
- *
- * The extracted value is validated against the configured test id format.
- * No additional normalization is applied to the returned value.
- *
- * If no valid test identifier is found in either source, the method returns {@code null}.
+ * Searches for the first tag that starts with the configured {@code TEST_ID_PREFIX}
+ * (case-insensitive), removes the prefix, and validates the remaining value.
*
- * @param sr the {@link ScenarioRuntime} representing the executed Karate test case
- * @return the extracted test identifier, or {@code null} if not found
+ * @param sr the {@link ScenarioRuntime} of the executed Karate scenario
+ * @return the extracted test identifier, or {@code null} if none is found
*/
public String extractTestId(ScenarioRuntime sr) {
- String testId = findFirstValidTestId(
- Optional.ofNullable(sr.scenario.getExampleData())
- .stream()
- .flatMap(m -> m.entrySet().stream())
- .filter(e -> e.getKey().equalsIgnoreCase(TEST_ID_PREFIX))
- .map(Map.Entry::getValue)
- .map(Object::toString)
- );
-
- if (isNull(testId)) {
- testId = findFirstValidTestId(sr.tags.getTags().stream()
- .filter(Objects::nonNull)
- .filter(tag -> tag.regionMatches(true, 0,
- TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
- .map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));
- }
+ return findFirstValidTestId(sr.tags.getTags().stream()
+ .filter(Objects::nonNull)
+ .filter(tag -> tag.regionMatches(true, 0,
+ TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
+ .map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));
- return testId;
}
/**
diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/hooks/KarateHook.java b/java-reporter-karate/src/main/java/io/testomat/karate/hooks/KarateHook.java
index 207112df..76d3a912 100644
--- a/java-reporter-karate/src/main/java/io/testomat/karate/hooks/KarateHook.java
+++ b/java-reporter-karate/src/main/java/io/testomat/karate/hooks/KarateHook.java
@@ -5,30 +5,49 @@
import com.intuit.karate.RuntimeHook;
import com.intuit.karate.Suite;
import com.intuit.karate.core.ScenarioRuntime;
+import com.intuit.karate.core.Step;
+import com.intuit.karate.core.StepResult;
import io.testomat.core.exception.ReportTestResultException;
import io.testomat.core.model.TestResult;
import io.testomat.core.runmanager.GlobalRunManager;
+import io.testomat.core.step.StepStorage;
+import io.testomat.core.step.StepTimer;
+import io.testomat.core.step.TestStep;
+import io.testomat.karate.adapter.CustomKarateEngineAdapter;
+import io.testomat.karate.adapter.KarateEngineAdapter;
import io.testomat.karate.constructor.KarateTestResultConstructor;
import io.testomat.karate.exception.KarateHookException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Runtime hook for integrating Karate test execution with Testomat.io.
- * Reports Karate test execution results to the Testomat.io platform.
+ * Reports Karate test execution results to the
+ * Testomat.io platform.
*/
public class KarateHook implements RuntimeHook {
+ private static final Logger log = LoggerFactory.getLogger(KarateHook.class);
+
+ private static final String LOG_NEXT_STEP = "log_next_step";
+ private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";
+ private static final String LOG_STEPS = "LogSteps";
+
private final KarateTestResultConstructor resultConstructor;
private final FacadeFunctionsHandler functionsHandler;
private final GlobalRunManager runManager;
+ private final KarateEngineAdapter engine;
public KarateHook(
KarateTestResultConstructor resultConstructor,
FacadeFunctionsHandler functionsHandler,
- GlobalRunManager runManager
+ GlobalRunManager runManager,
+ KarateEngineAdapter engine
) {
this.resultConstructor = resultConstructor;
this.functionsHandler = functionsHandler;
this.runManager = runManager;
+ this.engine = engine;
}
/**
@@ -39,7 +58,8 @@ public KarateHook() {
this(
new KarateTestResultConstructor(),
new FacadeFunctionsHandler(),
- GlobalRunManager.getInstance()
+ GlobalRunManager.getInstance(),
+ new CustomKarateEngineAdapter()
);
}
@@ -48,6 +68,51 @@ public void beforeSuite(Suite suite) {
runManager.incrementSuiteCounter();
}
+ @Override
+ public boolean beforeStep(Step step, ScenarioRuntime sr) {
+ boolean isMarked = Boolean.TRUE.equals(engine.getVariable(LOG_NEXT_STEP));
+
+ boolean isDslStep = switch (step.getPrefix() == null ? "" : step.getPrefix()) {
+ case "Given", "When", "Then", "And", "But" -> true;
+ default -> false;
+ };
+
+ boolean logAllSteps = isDslStep && sr.tags.getTags().contains(LOG_STEPS);
+
+ if (logAllSteps || isMarked) {
+ engine.setVariable(LOG_NEXT_STEP, false);
+ String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
+ StepTimer.start(stepId);
+ }
+ return true;
+ }
+
+ @Override
+ public void afterStep(StepResult result, ScenarioRuntime sr) {
+ Step step = result.getStep();
+ String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
+ long durationMillis = StepTimer.stop(stepId);
+
+ if (durationMillis != -1) {
+ String stepName = step.getText();
+ Object title = engine.getVariable(LOG_NEXT_STEP_TITLE);
+
+ if (title != null) {
+ stepName = title.toString();
+ engine.setVariable(LOG_NEXT_STEP_TITLE, null);
+ }
+
+ TestStep testStep = new TestStep();
+ testStep.setCategory("user");
+ testStep.setStepTitle(stepName);
+ testStep.setDuration(durationMillis);
+
+ StepStorage.addStep(testStep);
+
+ log.debug("Step '{}' completed in {} ms", stepName, durationMillis);
+ }
+ }
+
@Override
public void afterScenario(ScenarioRuntime sr) {
if (!runManager.isActive()) {
diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/marker/StepMarker.java b/java-reporter-karate/src/main/java/io/testomat/karate/marker/StepMarker.java
new file mode 100644
index 00000000..275e4106
--- /dev/null
+++ b/java-reporter-karate/src/main/java/io/testomat/karate/marker/StepMarker.java
@@ -0,0 +1,28 @@
+package io.testomat.karate.marker;
+
+import com.intuit.karate.core.ScenarioEngine;
+
+public final class StepMarker {
+
+ private static final String LOG_NEXT_STEP = "log_next_step";
+ private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";
+
+ private StepMarker() {
+
+ }
+
+ public static void mark() {
+ ScenarioEngine engine = ScenarioEngine.get();
+ if (engine != null) {
+ engine.setVariable(LOG_NEXT_STEP, true);
+ }
+ }
+
+ public static void mark(String title) {
+ ScenarioEngine engine = ScenarioEngine.get();
+ if (engine != null) {
+ engine.setVariable(LOG_NEXT_STEP, true);
+ engine.setVariable(LOG_NEXT_STEP_TITLE, title);
+ }
+ }
+}
diff --git a/java-reporter-karate/src/test/java/io/testomat/karate/adapter/CustomKarateEngineAdapterTest.java b/java-reporter-karate/src/test/java/io/testomat/karate/adapter/CustomKarateEngineAdapterTest.java
new file mode 100644
index 00000000..282ec937
--- /dev/null
+++ b/java-reporter-karate/src/test/java/io/testomat/karate/adapter/CustomKarateEngineAdapterTest.java
@@ -0,0 +1,70 @@
+package io.testomat.karate.adapter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.intuit.karate.core.ScenarioEngine;
+import com.intuit.karate.core.Variable;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+class CustomKarateEngineAdapterTest {
+
+ CustomKarateEngineAdapter adapter = new CustomKarateEngineAdapter();
+
+ @Test
+ void shouldReturnVariableValue() throws Exception {
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+ Variable variable = mock(Variable.class);
+
+ when(variable.getValue()).thenReturn("value");
+
+ Map vars = new HashMap<>();
+ vars.put("test", variable);
+
+ Field varsField = ScenarioEngine.class.getDeclaredField("vars");
+ varsField.setAccessible(true);
+ varsField.set(engine, vars);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+ Object result = adapter.getVariable("test");
+ assertEquals("value", result);
+ }
+ }
+
+ @Test
+ void shouldReturnNullWhenVariableMissing() throws Exception {
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ Map vars = new HashMap<>();
+
+ Field varsField = ScenarioEngine.class.getDeclaredField("vars");
+ varsField.setAccessible(true);
+ varsField.set(engine, vars);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+ Object result = adapter.getVariable("missing");
+ assertNull(result);
+ }
+ }
+
+ @Test
+ void shouldSetVariable() {
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+ adapter.setVariable("key", "value");
+ verify(engine).setVariable("key", "value");
+ }
+ }
+}
diff --git a/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java b/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java
index 99b5da0c..8e7330a3 100644
--- a/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java
+++ b/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java
@@ -12,8 +12,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
@@ -59,83 +57,6 @@ void extractExceptionDetailsWhenPassed() {
.isEqualTo(ExceptionDetails.empty());
}
- @Test
- void extractTestIdFromExampleDataHasPriorityOverTags() {
- ScenarioRuntime sr = mockScenarioRuntimeWithTags("@testId:Taaaa1111");
-
- Map exampleData = new HashMap<>();
- exampleData.put("testId", "Tbbbb2222");
-
- when(sr.scenario.getExampleData()).thenReturn(exampleData);
-
- String testId = extractor.extractTestId(sr);
-
- assertThat(testId).isEqualTo("Tbbbb2222");
- }
-
- @Test
- void extractTestIdFromExampleDataOnly() {
- ScenarioRuntime sr = mockScenarioRuntime();
-
- Map exampleData = new HashMap<>();
- exampleData.put("testId", "Tabcd1234");
-
- when(sr.scenario.getExampleData()).thenReturn(exampleData);
-
- String testId = extractor.extractTestId(sr);
-
- assertThat(testId).isEqualTo("Tabcd1234");
- }
-
- @Test
- void extractTestIdFromTagsWhenExampleDataIsNull() {
- ScenarioRuntime sr =
- mockScenarioRuntimeWithTags("@testId:Tabc12345", "smoke");
-
- when(sr.scenario.getExampleData()).thenReturn(null);
-
- String testId = extractor.extractTestId(sr);
-
- assertThat(testId).isEqualTo("Tabc12345");
- }
-
- @Test
- void extractTestIdFromTagsWhenExampleDataDoesNotContainTestId() {
- ScenarioRuntime sr =
- mockScenarioRuntimeWithTags("@testId:Tcccc3333");
-
- Map exampleData = new HashMap<>();
- exampleData.put("other", "value");
-
- when(sr.scenario.getExampleData()).thenReturn(exampleData);
-
- String testId = extractor.extractTestId(sr);
-
- assertThat(testId).isEqualTo("Tcccc3333");
- }
-
- @Test
- void extractTestIdReturnsNullWhenNotFoundAnywhere() {
- ScenarioRuntime sr =
- mockScenarioRuntimeWithTags("smoke", "regression");
-
- when(sr.scenario.getExampleData()).thenReturn(null);
-
- assertThat(extractor.extractTestId(sr)).isNull();
- }
-
- @Test
- void extractTestIdIgnoresInvalidFormat() {
- ScenarioRuntime sr =
- mockScenarioRuntimeWithTags("testId:@W11112222", "smoke");
-
- when(sr.scenario.getExampleData()).thenReturn(null);
-
- String testId = extractor.extractTestId(sr);
-
- assertThat(testId).isNull();
- }
-
@Test
void extractTestIdNotFound() {
ScenarioRuntime sr = mockScenarioRuntimeWithTags("smoke", "regression");
diff --git a/java-reporter-karate/src/test/java/io/testomat/karate/hooks/KarateHookTest.java b/java-reporter-karate/src/test/java/io/testomat/karate/hooks/KarateHookTest.java
index 6944975b..63ae9a75 100644
--- a/java-reporter-karate/src/test/java/io/testomat/karate/hooks/KarateHookTest.java
+++ b/java-reporter-karate/src/test/java/io/testomat/karate/hooks/KarateHookTest.java
@@ -1,28 +1,48 @@
package io.testomat.karate.hooks;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import com.intuit.karate.Suite;
import com.intuit.karate.core.Scenario;
+import com.intuit.karate.core.ScenarioEngine;
import com.intuit.karate.core.ScenarioRuntime;
+import com.intuit.karate.core.Step;
+import com.intuit.karate.core.StepResult;
+import com.intuit.karate.core.Tags;
import io.testomat.core.exception.ReportTestResultException;
import io.testomat.core.model.TestResult;
import io.testomat.core.runmanager.GlobalRunManager;
+import io.testomat.core.step.StepStorage;
+import io.testomat.core.step.StepTimer;
+import io.testomat.core.step.TestStep;
+import io.testomat.karate.adapter.CustomKarateEngineAdapter;
+import io.testomat.karate.adapter.KarateEngineAdapter;
import io.testomat.karate.constructor.KarateTestResultConstructor;
import io.testomat.karate.exception.KarateHookException;
import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
class KarateHookTest {
KarateTestResultConstructor constructor = mock(KarateTestResultConstructor.class);
FacadeFunctionsHandler functionsHandler = mock(FacadeFunctionsHandler.class);
GlobalRunManager runManager = mock(GlobalRunManager.class);
+ CustomKarateEngineAdapter engine = mock(CustomKarateEngineAdapter.class);
ScenarioRuntime sr = mock(ScenarioRuntime.class);
TestResult testResult = mock(TestResult.class);
@@ -31,7 +51,7 @@ class KarateHookTest {
@BeforeEach
void setUp() {
- hook = new KarateHook(constructor, functionsHandler, runManager);
+ hook = new KarateHook(constructor, functionsHandler, runManager, engine);
}
@Test
@@ -95,6 +115,154 @@ void shouldWrapExceptionWhenReportFails() {
verify(functionsHandler).handleFacadeFunctions(sr);
}
+ @Test
+ void shouldStartTimerWhenLogNextStepIsTrue() {
+ Step step = mock(Step.class);
+ when(step.getPrefix()).thenReturn("Given");
+
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ Tags tags = mock(Tags.class);
+ when(tags.getTags()).thenReturn(Collections.emptyList());
+ injectFinalField(sr, "tags", tags);
+
+ KarateEngineAdapter engine = mock(KarateEngineAdapter.class);
+ when(engine.getVariable("log_next_step")).thenReturn(true);
+
+ injectFinalField(hook, "engine", engine);
+
+ try (MockedStatic timer = mockStatic(StepTimer.class)) {
+ boolean result = hook.beforeStep(step, sr);
+
+ assertTrue(result);
+
+ verify(engine).setVariable("log_next_step", false);
+ timer.verify(() -> StepTimer.start(any()), times(1));
+ }
+ }
+
+ @Test
+ void shouldStartTimerWhenLogStepsTagPresent() {
+ Step step = mock(Step.class);
+ when(step.getPrefix()).thenReturn("When");
+
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ Tags tags = mock(Tags.class);
+ when(tags.getTags()).thenReturn(List.of("LogSteps"));
+ injectFinalField(sr, "tags", tags);
+
+ KarateEngineAdapter engine = mock(KarateEngineAdapter.class);
+ when(engine.getVariable("log_next_step")).thenReturn(false);
+
+ injectFinalField(hook, "engine", engine);
+
+ try (MockedStatic timer = mockStatic(StepTimer.class)) {
+
+ hook.beforeStep(step, sr);
+
+ verify(engine).setVariable("log_next_step", false);
+ timer.verify(() -> StepTimer.start(any()), times(1));
+ }
+ }
+
+ @Test
+ void shouldNotStartTimerWhenStepIsNotDslAndNoFlags() {
+ Step step = mock(Step.class);
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ when(step.getPrefix()).thenReturn("*");
+
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+
+ boolean result = hook.beforeStep(step, sr);
+
+ assertTrue(result);
+ verify(engine, never()).setVariable(eq("log_next_step"), any());
+ }
+ }
+
+ @Test
+ void shouldSkipStepIfTimerNotStarted() {
+ StepResult result = mock(StepResult.class);
+ Step step = mock(Step.class);
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ when(result.getStep()).thenReturn(step);
+
+ try (MockedStatic timer = mockStatic(StepTimer.class)) {
+ timer.when(() -> StepTimer.stop(any())).thenReturn(-1L);
+
+ hook.afterStep(result, sr);
+
+ timer.verify(() -> StepTimer.stop(any()));
+ }
+ }
+
+ @Test
+ void shouldStoreStepWhenTimerStopped() {
+ StepResult result = mock(StepResult.class);
+ Step step = mock(Step.class);
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ when(result.getStep()).thenReturn(step);
+ when(step.getText()).thenReturn("Some step");
+
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ try (MockedStatic mockedEngine = mockStatic(ScenarioEngine.class);
+ MockedStatic mockedTimer = mockStatic(StepTimer.class);
+ MockedStatic storage = mockStatic(StepStorage.class)) {
+
+ mockedEngine.when(ScenarioEngine::get).thenReturn(engine);
+ mockedTimer.when(() -> StepTimer.stop(any())).thenReturn(100L);
+
+ hook.afterStep(result, sr);
+
+ storage.verify(() -> StepStorage.addStep(any(TestStep.class)));
+ }
+ }
+
+ @Test
+ void shouldUseCustomStepTitle() {
+
+ StepResult result = mock(StepResult.class);
+ Step step = mock(Step.class);
+ ScenarioRuntime sr = mock(ScenarioRuntime.class);
+
+ when(result.getStep()).thenReturn(step);
+ when(step.getText()).thenReturn("Original");
+
+ KarateEngineAdapter engine = mock(KarateEngineAdapter.class);
+
+ when(engine.getVariable("log_next_step_title"))
+ .thenReturn("Custom title");
+
+ try (MockedStatic timer = mockStatic(StepTimer.class);
+ MockedStatic storage = mockStatic(StepStorage.class)) {
+
+ timer.when(() -> StepTimer.stop(any())).thenReturn(50L);
+
+ KarateHook hook = new KarateHook(
+ constructor,
+ functionsHandler,
+ runManager,
+ engine
+ );
+
+ hook.afterStep(result, sr);
+
+ storage.verify(() -> StepStorage.addStep(
+ argThat(s -> s.getStepTitle().equals("Custom title"))
+ ));
+
+ verify(engine).setVariable("log_next_step_title", null);
+ }
+ }
+
static void injectFinalField(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
@@ -104,8 +272,4 @@ static void injectFinalField(Object target, String fieldName, Object value) {
throw new RuntimeException(e);
}
}
-
-
-
}
-
diff --git a/java-reporter-karate/src/test/java/io/testomat/karate/marker/StepMarkerTest.java b/java-reporter-karate/src/test/java/io/testomat/karate/marker/StepMarkerTest.java
new file mode 100644
index 00000000..a5a764e7
--- /dev/null
+++ b/java-reporter-karate/src/test/java/io/testomat/karate/marker/StepMarkerTest.java
@@ -0,0 +1,60 @@
+package io.testomat.karate.marker;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import com.intuit.karate.core.ScenarioEngine;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+class StepMarkerTest {
+
+ private static final String LOG_NEXT_STEP = "log_next_step";
+ private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";
+ private static final String MY_STEP = "My step";
+ private static final String TITLE = "title";
+
+ @Test
+ void shouldMarkNextStep() {
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+
+ StepMarker.mark();
+
+ verify(engine).setVariable(LOG_NEXT_STEP, true);
+ verifyNoMoreInteractions(engine);
+ }
+ }
+
+ @Test
+ void shouldMarkNextStepWithTitle() {
+ ScenarioEngine engine = mock(ScenarioEngine.class);
+
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(engine);
+
+ StepMarker.mark(MY_STEP);
+
+ verify(engine).setVariable(LOG_NEXT_STEP, true);
+ verify(engine).setVariable(LOG_NEXT_STEP_TITLE, MY_STEP);
+ verifyNoMoreInteractions(engine);
+ }
+ }
+
+ @Test
+ void shouldDoNothingIfEngineIsNull() {
+ try (MockedStatic mocked = mockStatic(ScenarioEngine.class)) {
+ mocked.when(ScenarioEngine::get).thenReturn(null);
+
+ StepMarker.mark();
+ StepMarker.mark(TITLE);
+
+ mocked.verify(ScenarioEngine::get, times(2));
+ }
+ }
+}
diff --git a/java-reporter-testng/pom.xml b/java-reporter-testng/pom.xml
index f6db8543..9204f4a6 100644
--- a/java-reporter-testng/pom.xml
+++ b/java-reporter-testng/pom.xml
@@ -6,7 +6,7 @@
io.testomat
java-reporter-testng
- 0.7.9
+ 0.7.10
jar
Testomat.io Java Reporter TestNG
@@ -47,7 +47,7 @@
io.testomat
java-reporter-core
- 0.9.0
+ 0.9.1
org.slf4j
diff --git a/pom.xml b/pom.xml
index 1c0d3a78..45e6c1eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,11 +6,11 @@
io.testomat
java-reporter
- 0.1.0
+ 0.2.0
pom
Testomat.io Java Reporter
- Java reporter library for Testomat.io integration with JUnit 5, TestNG, and Cucumber support
+ Java reporter library for Testomat.io integration with JUnit 5, TestNG, Cucumber, and Karate support
https://github.com/testomatio/java-reporter