diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20f3334..60efaa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,10 +33,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '11' @@ -45,4 +45,6 @@ jobs: run: ./gradlew build - name: Codecov upload - run: bash <(curl -s https://codecov.io/bash) + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6b1aae4..1bb112b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,13 +30,16 @@ env: CHANGE_LOG_FILE: CHANGELOG.md CHANGE_LOG_TMP_FILE: CHANGELOG_updated.md REPOSITORY_URL: 'https://maven.pkg.github.com/' + README_FILE: README.md + README_TEMPLATE_FILE: README_TEMPLATE.md + README_VERSION_PLACEHOLDER: $LATEST_VERSION jobs: release: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Generate versions uses: HardNorth/github-version-generate@v1 @@ -46,7 +49,7 @@ jobs: version-file-extraction-pattern: ${{ env.VERSION_EXTRACT_PATTERN }} - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '11' @@ -66,6 +69,13 @@ jobs: -PgithubUserName=${{ github.actor }} -PgithubToken=${{ secrets.GITHUB_TOKEN }} \ -PgpgPassphrase=${{ secrets.GPG_PASSPHRASE }} -PgpgPrivateKey="${{ secrets.GPG_PRIVATE_KEY }}" + - name: Update README.md + id: readmeUpdate + run: | + sed 's/${{ env.README_VERSION_PLACEHOLDER }}/${{ env.RELEASE_VERSION }}/g' ${{ env.README_TEMPLATE_FILE }} > ${{ env.README_FILE }} + git add ${{ env.README_FILE }} + git commit -m "Readme update" + - name: Update CHANGELOG.md id: changelogUpdate run: | @@ -96,17 +106,17 @@ jobs: body: ${{ steps.readChangelogEntry.outputs.changes }} - name: Checkout develop branch - if: ${{github.ref}} == 'master' - uses: actions/checkout@v3 + if: ${{github.ref}} == 'main' + uses: actions/checkout@v4 with: ref: 'develop' fetch-depth: 0 - name: Merge release branch into develop id: mergeIntoDevelop - if: ${{github.ref}} == 'master' + if: ${{github.ref}} == 'main' run: | - git merge -m 'Merge master branch into develop after a release' origin/master + git merge -m 'Merge main branch into develop after a release' origin/main git status | (! grep -Fq 'both modified:') || git status | grep -F 'both modified:' \ - | { echo -e 'Unable to merge master into develop, merge conflicts:'; (! grep -Eo '[^ ]+$') } + | { echo -e 'Unable to merge main into develop, merge conflicts:'; (! grep -Eo '[^ ]+$') } git push origin develop diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb72e1..013f86b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ # Changelog ## [Unreleased] +### Changed +- Karate dependency marked as `compileOnly` to force users specify their own version of Karate, by @HardNorth +- Client version updated on [5.2.5](https://github.com/reportportal/client-java/releases/tag/5.2.5), by @HardNorth +### Fixed +- Issue [#23](https://github.com/reportportal/agent-java-karate/issues/23) scenarios outside features in parallel execution, by @HardNorth ## [5.0.0] +### Added +- Basic Agent functionality, by @vrymar ### Changed - Refactored and implemented main ReportPortal agent features, by @HardNorth diff --git a/README.md b/README.md index 5167357..978cc72 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,177 @@ -# agent-java-karate - -- [Description](#description) -- [Agent Configuration](#agent-configuration) -- [Properties File Configuration](#properties-file-configuration) -- [Execution](#execution) - - -## Description -ReportPortal Java agent for Karate testing tool. - -## Agent Configuration -Until the agent-java-karate project is published to Maven repository, -the following configuration can be used: - -* Add jitpack repository to get agent project from GitHub: - * Maven pom.xml - ```xml - - - jitpack.io - https://jitpack.io - - - ``` - - * Gradle build.gradle - ```groovy - repositories { - maven { url 'https://jitpack.io' } - } - ``` - -* Add dependency for the agent project: - * Maven pom.xml - ```xml - - com.github.vrymar - agent-java-karate - Tag_or_Version - - ``` - - * Gradle build.gradle - ```groovy - implementation 'com.github.vrymar:agent-java-karate:Tag_or_Version' - ``` - - -* Add reportportal `client-java` dependency: - * Maven pom.xml - ```xml +# ReportPortal runtime Hook for Karate tests + +Karate reporters which uploads the results to a ReportPortal server. + +> **DISCLAIMER**: We use Google Analytics for sending anonymous usage information such as agent's and client's names, and their versions +> after a successful launch start. This information might help us to improve both ReportPortal backend and client sides. It is used by the +> ReportPortal team only and is not supposed for sharing with 3rd parties. + +[![Maven Central](https://img.shields.io/maven-central/v/com.epam.reportportal/agent-java-karate.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/com.epam.reportportal/agent-java-karate) +[![CI Build](https://github.com/reportportal/agent-java-karate/actions/workflows/ci.yml/badge.svg)](https://github.com/reportportal/agent-java-karate/actions/workflows/ci.yml) +[![codecov](https://codecov.io/github/reportportal/agent-java-karate/graph/badge.svg?token=wJr9F6hZln)](https://codecov.io/github/reportportal/agent-java-karate) +[![Join Slack chat!](https://img.shields.io/badge/slack-join-brightgreen.svg)](https://slack.epmrpp.reportportal.io/) +[![stackoverflow](https://img.shields.io/badge/reportportal-stackoverflow-orange.svg?style=flat)](http://stackoverflow.com/questions/tagged/reportportal) +[![Build with Love](https://img.shields.io/badge/build%20with-❤%EF%B8%8F%E2%80%8D-lightgrey.svg)](http://reportportal.io?style=flat) + +The latest version: 5.0.0. Please use `Maven Central` link above to get the agent. + +## Overview: How to Add ReportPortal Logging to Your Project + +To start using ReportPortal with Karate framework please do the following steps: + +1. [Configuration](#configuration) + * Create/update the `reportportal.properties` configuration file + * Build system configuration + * Add Listener + * Runtime + * Post-running +2. [Logging configuration](#logging) + * Loggers and their types +3. [Running tests](#running-tests) + * Build system commands + +## Configuration + +### 'reportportal.properties' configuration file + +As the first step you need to create a file named `reportportal.properties` in your Java project in a source +folder `src/main/resources` or `src/test/resources` (depending on where your tests are located): + +**reportportal.properties** + +``` +rp.endpoint = http://localhost:8080 +rp.api.key = test_YIvQraKKSquDZqrA6JLCWCX5qwmMZBk_7tTm_fkN44AHCi18Ze0RtYqxWNYKxk5p +rp.launch = Karate Tests +rp.project = default_personal +``` + +**Property description** + +* `rp.endpoint` - the URL for the ReportPortal server (actual link). +* `rp.api.key` - an access token for ReportPortal which is used for user identification. It can be found on your report + portal user profile page. +* `rp.project` - a project ID on which the agent will report test launches. Must be set to one of your assigned + projects. +* `rp.launch` - a user-selected identifier of test launches. + +The full list of supported properties is located here in client-java library documentation (a common library for all +Java agents): https://github.com/reportportal/client-java + +## Build system configuration + +### Maven + +If your project is Maven-based you need to add dependencies to `pom.xml` file: + +```xml + + + + - com.epam.reportportal - client-java - Tag_or_Version + com.epam.reportportal + agent-java-karate + 5.0.0 + test - ``` - * Gradle build.gradle - ```groovy - implementation 'com.epam.reportportal:client-java:Tag_or_Version' - ``` - - -**Note**: When the agent is approved by ReportPortal, -the agent repository can be taken from `reportportal`. E.g.: -* Maven pom.xml - ```xml - - com.github.reportportal - agent-java-karate - Tag_or_Version - - ``` - -* Gradle build.gradle - ```groovy - implementation 'com.github.reportportal:agent-java-karate:Tag_or_Version' - ``` - -## Properties File Configuration -* Create `reportportal.properties` file in `src\main\resources` directory. -* Add the following parameters: + + + ``` -rp.endpoint = -rp.uuid = -rp.launch = -rp.project = - -OPTIONAL PARAMETERS -rp.reporting.async=true -rp.reporting.callback=true -rp.enable=true -rp.description=My awesome launch -rp.attributes=key:value;value -rp.rerun=true -rp.rerun.of=ae586912-841c-48de-9391-50720c35dd5a -rp.convertimage=true -rp.mode=DEFAULT -rp.skipped.issue=true -rp.batch.size.logs=20 -rp.keystore.resource= -rp.keystore.password= + +You are free to use you own version of Karate, but not earlier than 1.0.0. If you leave just Agent dependency it will +be still OK, it will use transitive Karate version. + +### Gradle + +For Gradle-based projects please update dependencies section in `build.gradle` file: + +```groovy +dependencies { + testImplementation 'com.epam.reportportal:agent-java-karate:5.0.0' +} ``` -## Execution -To publish test results to ReportPortal, the test project should run by `KarateReportPortalRunner` instead of Karate runner. -E.g.: - - ```java - class scenarioRunnerTest { - - @Test - void testParallel() { - KarateReportPortalRunner - .path("classpath:examples") - .outputCucumberJson(true) - .tags("~@ignore") - .parallel(1); - } - } - ``` +## Listener configuration + +### Runtime + +Runtime publisher uploads Karate tests on ReportPortal during the test execution, providing real-time monitoring capabilities. To publish +test results in this case, the test project should use by `ReportPortalHook` class, an instance of which you should pass to Karate runner. +E.G.: + +```java +import com.epam.reportportal.karate.ReportPortalHook; +import com.intuit.karate.Runner; + +class ScenarioRunnerTest { + @Test + void testParallel() { + return Runner + .path("classpath:examples") + .hook(new ReportPortalHook()) + .outputCucumberJson(true) + .tags("~@ignore") + .parallel(1); + } +} +``` + +### Post-running + +Post-running publisher uploads Karate tests on ReportPortal after the test execution. It uses Karate result object to get data about tests. +It might be useful if your tests make heavy load both on ReportPortal server or on the running node. To publish test results in this case, +the test project should run by `KarateReportPortalRunner` instead of Karate runner. +E.G.: + +```java +import com.epam.reportportal.karate.KarateReportPortalRunner; + +class ScenarioRunnerTest { + @Test + void testParallel() { + KarateReportPortalRunner + .path("classpath:examples") + .outputCucumberJson(true) + .tags("~@ignore") + .parallel(1); + } +} +``` + +## Logging + +Karate uses `slf4j` as Logging library, so you are free to choose any Logging Framework. + +ReportPortal provides its own logger implementations for major logging frameworks like *Log4j* and *Logback*. It also +provides additional formatting features for popular client and test libraries like: *Selenide*, *Apache HttpComponents*, +*Rest Assured*, etc. + +Here is the list of supported loggers and setup documentation links. + +**Logging frameworks:** + +| **Library name** | **Documentation link** | +|------------------|-----------------------------------------------------| +| Log4j | https://github.com/reportportal/logger-java-log4j | +| Logback | https://github.com/reportportal/logger-java-logback | + +**HTTP clients:** + +| **Library name** | **Documentation link** | +|-----------------------|------------------------------------------------------------| +| OkHttp3 | https://github.com/reportportal/logger-java-okhttp3 | +| Apache HttpComponents | https://github.com/reportportal/logger-java-httpcomponents | + +## Running tests + +We are set. To run tests we just need to execute corresponding command in our build system. + +#### Maven + +`mvn test` or `mvnw test` if you are using Maven wrapper + +#### Gradle + +`gradle test` or `gradlew test` if you are using Gradle wrapper diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md index 79ab35e..40a15d9 100644 --- a/README_TEMPLATE.md +++ b/README_TEMPLATE.md @@ -9,7 +9,7 @@ Karate reporters which uploads the results to a ReportPortal server. [![Maven Central](https://img.shields.io/maven-central/v/com.epam.reportportal/agent-java-karate.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/com.epam.reportportal/agent-java-karate) [![CI Build](https://github.com/reportportal/agent-java-karate/actions/workflows/ci.yml/badge.svg)](https://github.com/reportportal/agent-java-karate/actions/workflows/ci.yml) [![codecov](https://codecov.io/github/reportportal/agent-java-karate/graph/badge.svg?token=wJr9F6hZln)](https://codecov.io/github/reportportal/agent-java-karate) -[![Join Slack chat!](https://slack.epmrpp.reportportal.io/badge.svg)](https://slack.epmrpp.reportportal.io/) +[![Join Slack chat!](https://img.shields.io/badge/slack-join-brightgreen.svg)](https://slack.epmrpp.reportportal.io/) [![stackoverflow](https://img.shields.io/badge/reportportal-stackoverflow-orange.svg?style=flat)](http://stackoverflow.com/questions/tagged/reportportal) [![Build with Love](https://img.shields.io/badge/build%20with-❤%EF%B8%8F%E2%80%8D-lightgrey.svg)](http://reportportal.io?style=flat) diff --git a/build.gradle b/build.gradle index a90e645..3d18514 100644 --- a/build.gradle +++ b/build.gradle @@ -42,12 +42,14 @@ repositories { } dependencies { - api 'com.epam.reportportal:client-java:5.2.0' - api "com.intuit.karate:karate-core:${project.karate_version}" + api 'com.epam.reportportal:client-java:5.2.5' + + compileOnly "com.intuit.karate:karate-core:${project.karate_version}" implementation 'org.slf4j:slf4j-api:2.0.7' - testImplementation 'com.epam.reportportal:logger-java-logback:5.2.0' - testImplementation 'com.epam.reportportal:agent-java-test-utils:0.0.2' + testImplementation "com.intuit.karate:karate-core:${project.karate_version}" + testImplementation 'com.epam.reportportal:logger-java-logback:5.2.1' + testImplementation 'com.epam.reportportal:agent-java-test-utils:0.0.3' testImplementation "org.junit.jupiter:junit-jupiter-api:${project.junit_version}" testImplementation "org.junit.jupiter:junit-jupiter-engine:${project.junit_version}" testImplementation "org.junit.jupiter:junit-jupiter-params:${project.junit_version}" diff --git a/gradle.properties b/gradle.properties index deb8a02..206f8c7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ name=agent-java-karate version=5.0.1-SNAPSHOT description=EPAM ReportPortal. Karate test framework [1.3.1, ) adapter gradle_version=8.2 -karate_version=[1.3.1, ) +karate_version=1.4.1 junit_version=5.10.1 mockito_version=5.4.0 scripts_url=https://raw.githubusercontent.com/reportportal/gradle-scripts diff --git a/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java b/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java index 1cc9530..f90b20f 100644 --- a/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java +++ b/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java @@ -16,6 +16,7 @@ package com.epam.reportportal.karate; +import com.epam.reportportal.karate.utils.BlockingConcurrentHashMap; import com.epam.reportportal.listeners.ItemStatus; import com.epam.reportportal.listeners.ListenerParameters; import com.epam.reportportal.listeners.LogLevel; @@ -55,7 +56,7 @@ public class ReportPortalHook implements RuntimeHook { private static final Logger LOGGER = LoggerFactory.getLogger(ReportPortalHook.class); protected final MemoizingSupplier launch; - private final Map> featureIdMap = new ConcurrentHashMap<>(); + private final BlockingConcurrentHashMap> featureIdMap = new BlockingConcurrentHashMap<>(); private final Map> scenarioIdMap = new ConcurrentHashMap<>(); private final Map> backgroundIdMap = new ConcurrentHashMap<>(); private final Map> stepIdMap = new ConcurrentHashMap<>(); @@ -150,9 +151,7 @@ protected StartTestItemRQ buildStartFeatureRq(@Nonnull FeatureRuntime fr) { @Override public boolean beforeFeature(FeatureRuntime fr) { - Maybe featureId = launch.get().startTestItem(buildStartFeatureRq(fr)); - Feature feature = fr.featureCall.feature; - featureIdMap.put(feature.getNameForReport(), featureId); + featureIdMap.computeIfAbsent(fr.featureCall.feature.getNameForReport(), f -> launch.get().startTestItem(buildStartFeatureRq(fr))); return true; } @@ -169,8 +168,7 @@ protected FinishTestItemRQ buildFinishFeatureRq(@Nonnull FeatureRuntime fr) { @Override public void afterFeature(FeatureRuntime fr) { - Feature feature = fr.featureCall.feature; - Maybe featureId = featureIdMap.remove(feature.getNameForReport()); + Maybe featureId = featureIdMap.remove(fr.featureCall.feature.getNameForReport()); if (featureId == null) { LOGGER.error("ERROR: Trying to finish unspecified feature."); } @@ -192,10 +190,9 @@ protected StartTestItemRQ buildStartScenarioRq(@Nonnull ScenarioRuntime sr) { @Override public boolean beforeScenario(ScenarioRuntime sr) { + Maybe featureId = featureIdMap.get(sr.featureRuntime.featureCall.feature.getNameForReport()); StartTestItemRQ rq = buildStartScenarioRq(sr); - - Maybe scenarioId = launch.get() - .startTestItem(featureIdMap.get(sr.featureRuntime.featureCall.feature.getNameForReport()), rq); + Maybe scenarioId = launch.get().startTestItem(featureId, rq); scenarioIdMap.put(sr.scenario.getUniqueId(), scenarioId); return true; } diff --git a/src/main/java/com/epam/reportportal/karate/utils/BlockingConcurrentHashMap.java b/src/main/java/com/epam/reportportal/karate/utils/BlockingConcurrentHashMap.java new file mode 100644 index 0000000..7394261 --- /dev/null +++ b/src/main/java/com/epam/reportportal/karate/utils/BlockingConcurrentHashMap.java @@ -0,0 +1,95 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.karate.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +public class BlockingConcurrentHashMap { + private static final Logger LOGGER = LoggerFactory.getLogger(BlockingConcurrentHashMap.class); + + private static final class BlockingReference { + private final BlockingQueue lock = new ArrayBlockingQueue<>(1); + private volatile boolean set = false; + private volatile T value; + + public void set(Function supplier) { + lock.clear(); + set = false; + try { + value = supplier.apply(null); + set = true; + try { + //noinspection StatementWithEmptyBody + while (lock.offer(value)) { + // Put while waiting Threads take values + } + } catch (IllegalStateException ignore) { + } + } catch (Exception e) { + LOGGER.warn("Unable to get result value from passed supplier.", e); + throw e; + } + } + + public T get(long timeout, TimeUnit unit) throws InterruptedException { + if (!set) { + return lock.poll(timeout, unit); + } else { + return value; + } + } + } + + private static final int TIMEOUT = 1; + private static final TimeUnit TIMEOUT_UNIT = TimeUnit.MINUTES; + + private final Map> map = new ConcurrentHashMap<>(); + + public void computeIfAbsent(@Nonnull K key, Function mappingFunction) { + map.computeIfAbsent(key, k -> new BlockingReference<>()).set(mappingFunction); + } + + @Nullable + public V get(@Nonnull K key) { + try { + return map.computeIfAbsent(key, k -> new BlockingReference<>()).get(TIMEOUT, TIMEOUT_UNIT); + } catch (InterruptedException e) { + LOGGER.warn("Wait for value was interrupted", e); + } + return null; + } + + @Nullable + public V remove(@Nonnull K key) { + try { + return map.remove(key).get(TIMEOUT, TIMEOUT_UNIT); + } catch (InterruptedException e) { + LOGGER.warn("Wait for value was interrupted", e); + } + return null; + } +} diff --git a/src/test/java/com/epam/reportportal/karate/status/OneExampleFailedTest.java b/src/test/java/com/epam/reportportal/karate/status/OneExampleFailedTest.java new file mode 100644 index 0000000..8dacfd8 --- /dev/null +++ b/src/test/java/com/epam/reportportal/karate/status/OneExampleFailedTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.karate.status; + +import com.epam.reportportal.karate.utils.TestUtils; +import com.epam.reportportal.listeners.ItemStatus; +import com.epam.reportportal.service.ReportPortal; +import com.epam.reportportal.service.ReportPortalClient; +import com.epam.reportportal.util.test.CommonUtils; +import com.epam.ta.reportportal.ws.model.FinishTestItemRQ; +import com.intuit.karate.Results; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.epam.reportportal.karate.utils.TestUtils.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +public class OneExampleFailedTest { + private static final String TEST_FEATURE = "classpath:feature/examples_one_failed.feature"; + private final String featureId = CommonUtils.namedId("feature_"); + private final List exampleIds = Stream.generate(() -> CommonUtils.namedId("example_")).limit(2).collect(Collectors.toList()); + private final List>> stepIds = exampleIds.stream() + .map(e -> Pair.of(e, Stream.generate(() -> CommonUtils.namedId("step_")).limit(2).collect(Collectors.toList()))) + .collect(Collectors.toList()); + private final ReportPortalClient client = mock(ReportPortalClient.class); + private final ReportPortal rp = ReportPortal.create(client, standardParameters(), testExecutor()); + + @BeforeEach + public void setupMock() { + mockLaunch(client, null, featureId, stepIds); + mockBatchLogging(client); + } + + private static void verifyStatus(List rqs, ItemStatus... statuses) { + for (int i = 0; i < rqs.size(); i++) { + ItemStatus statusTest = i >= statuses.length ? statuses[statuses.length - 1] : statuses[i]; + assertThat( + "Failed verifying request number: " + (i + 1), + rqs.get(i).getStatus(), + allOf(notNullValue(), equalTo(statusTest.name())) + ); + } + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + public void test_simple_one_step_failed(boolean report) { + Results results; + if (report) { + results = TestUtils.runAsReport(rp, TEST_FEATURE); + } else { + results = TestUtils.runAsHook(rp, TEST_FEATURE); + } + assertThat(results.getFailCount(), equalTo(1)); + + ArgumentCaptor featureCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(featureId), featureCaptor.capture()); + ArgumentCaptor firstExampleCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(exampleIds.get(0)), firstExampleCaptor.capture()); + ArgumentCaptor secondExampleCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(exampleIds.get(1)), secondExampleCaptor.capture()); + ArgumentCaptor firstExampleFirstStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(stepIds.get(0).getValue().get(0)), firstExampleFirstStepCaptor.capture()); + ArgumentCaptor firstExampleSecondStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(stepIds.get(0).getValue().get(1)), firstExampleSecondStepCaptor.capture()); + ArgumentCaptor secondExampleFirstStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(stepIds.get(1).getValue().get(0)), secondExampleFirstStepCaptor.capture()); + ArgumentCaptor secondExampleSecondStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client).finishTestItem(same(stepIds.get(1).getValue().get(1)), secondExampleSecondStepCaptor.capture()); + + FinishTestItemRQ featureRq = featureCaptor.getValue(); + assertThat(featureRq.getStatus(), allOf(notNullValue(), equalTo(ItemStatus.FAILED.name()))); + + verifyStatus(Arrays.asList(firstExampleCaptor.getValue(), secondExampleCaptor.getValue()), ItemStatus.PASSED, ItemStatus.FAILED); + + List steps = Arrays.asList(firstExampleFirstStepCaptor.getValue(), + firstExampleSecondStepCaptor.getValue(), + secondExampleFirstStepCaptor.getValue(), + secondExampleSecondStepCaptor.getValue() + ); + + verifyStatus(steps, ItemStatus.PASSED, ItemStatus.PASSED, ItemStatus.PASSED, ItemStatus.FAILED); + } +} diff --git a/src/test/resources/feature/examples_one_failed.feature b/src/test/resources/feature/examples_one_failed.feature new file mode 100644 index 0000000..76daf54 --- /dev/null +++ b/src/test/resources/feature/examples_one_failed.feature @@ -0,0 +1,10 @@ +Feature: math tests with examples + + Scenario Outline: Verify different maths + Given def mathResult = vara + varb + Then assert mathResult == result + + Examples: + | vara! | varb! | result! | + | 2 | 2 | 4 | + | 1 | 2 | 4 |