Skip to content

Commit be543f0

Browse files
author
Dmytro Chyzhykov
committed
The first TestNG "native" integration.
The simplest usage: import cucumber.api.testng.AbstractTestNGCucumberTests; public class RunCukesTest extends AbstractTestNGCucumberTests { } Note: Maybe there should be a more appropriate TestNG reporter for Cucumber.
1 parent 337c9e5 commit be543f0

File tree

8 files changed

+306
-0
lines changed

8 files changed

+306
-0
lines changed

testng/pom.xml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>info.cukes</groupId>
6+
<artifactId>cucumber-jvm</artifactId>
7+
<relativePath>../pom.xml</relativePath>
8+
<version>1.1.4-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>cucumber-testng</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Cucumber-JVM: TestNG</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>info.cukes</groupId>
18+
<artifactId>cucumber-junit</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.testng</groupId>
22+
<artifactId>testng</artifactId>
23+
</dependency>
24+
</dependencies>
25+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package cucumber.api.testng;
2+
3+
import cucumber.runtime.Runtime;
4+
import cucumber.runtime.RuntimeOptions;
5+
import cucumber.runtime.io.MultiLoader;
6+
import cucumber.runtime.io.ResourceLoader;
7+
import cucumber.runtime.junit.RuntimeOptionsFactory;
8+
import org.testng.IHookCallBack;
9+
import org.testng.IHookable;
10+
import org.testng.ITestResult;
11+
import org.testng.annotations.Test;
12+
13+
public abstract class AbstractTestNGCucumberTests implements IHookable {
14+
private TestNgReporter reporter;
15+
private Runtime runtime;
16+
private ClassLoader classLoader;
17+
private ResourceLoader resourceLoader;
18+
19+
public AbstractTestNGCucumberTests() {
20+
classLoader = getClass().getClassLoader();
21+
resourceLoader = new MultiLoader(classLoader);
22+
23+
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(getClass());
24+
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();
25+
26+
reporter = new TestNgReporter(System.out);
27+
runtimeOptions.formatters.add(reporter);
28+
runtime = new Runtime(resourceLoader, classLoader, runtimeOptions);
29+
}
30+
31+
@Test(groups = "cucumber", description = "Runs Cucumber Features")
32+
public void run_cukes() {
33+
runtime.run();
34+
35+
if (!runtime.getErrors().isEmpty()) {
36+
throw new RuntimeException(runtime.getErrors().get(0));
37+
}
38+
}
39+
40+
@Override
41+
public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) {
42+
iHookCallBack.runTestMethod(iTestResult);
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package cucumber.api.testng;
2+
3+
import gherkin.formatter.Formatter;
4+
import gherkin.formatter.NiceAppendable;
5+
import gherkin.formatter.Reporter;
6+
import gherkin.formatter.model.Background;
7+
import gherkin.formatter.model.Examples;
8+
import gherkin.formatter.model.Feature;
9+
import gherkin.formatter.model.Match;
10+
import gherkin.formatter.model.Result;
11+
import gherkin.formatter.model.Scenario;
12+
import gherkin.formatter.model.ScenarioOutline;
13+
import gherkin.formatter.model.Step;
14+
import org.testng.ITestResult;
15+
16+
import java.util.LinkedList;
17+
import java.util.List;
18+
import java.util.concurrent.atomic.AtomicInteger;
19+
20+
import static org.testng.Reporter.getCurrentTestResult;
21+
import static org.testng.Reporter.log;
22+
23+
public class TestNgReporter implements Formatter, Reporter {
24+
25+
private final NiceAppendable out;
26+
27+
private AtomicInteger failureCount = new AtomicInteger(0);
28+
private AtomicInteger skipCount = new AtomicInteger(0);
29+
private AtomicInteger passCount = new AtomicInteger(0);
30+
31+
private LinkedList<Step> steps = new LinkedList<Step>();
32+
33+
public TestNgReporter(Appendable appendable) {
34+
out = new NiceAppendable(appendable);
35+
}
36+
37+
@Override
38+
public void uri(String uri) {
39+
logDiv("Feature File: " + uri, "featureFile");
40+
}
41+
42+
@Override
43+
public void feature(Feature feature) {
44+
logDiv("Feature: " + feature.getName(), "feature");
45+
}
46+
47+
@Override
48+
public void background(Background background) {
49+
}
50+
51+
@Override
52+
public void scenario(Scenario scenario) {
53+
logDiv("Scenario: " + scenario.getName(), "scenario");
54+
}
55+
56+
@Override
57+
public void scenarioOutline(ScenarioOutline scenarioOutline) {
58+
logDiv("Scenario Outline: " + scenarioOutline.getName(), "scenarioOutline");
59+
}
60+
61+
@Override
62+
public void examples(Examples examples) {
63+
}
64+
65+
@Override
66+
public void step(Step step) {
67+
steps.add(step);
68+
}
69+
70+
@Override
71+
public void eof() {
72+
}
73+
74+
@Override
75+
public void syntaxError(String s, String s2, List<String> strings, String s3, Integer integer) {
76+
}
77+
78+
@Override
79+
public void done() {
80+
steps.clear();
81+
}
82+
83+
@Override
84+
public void close() {
85+
out.close();
86+
}
87+
88+
@Override
89+
public void before(Match match, Result result) {
90+
}
91+
92+
@Override
93+
public void result(Result result) {
94+
logResult(result);
95+
96+
if (Result.FAILED.equals(result.getStatus())) {
97+
ITestResult tr = getCurrentTestResult();
98+
tr.setThrowable(result.getError());
99+
tr.setStatus(ITestResult.FAILURE);
100+
failureCount.incrementAndGet();
101+
} else if (Result.SKIPPED.equals(result)) {
102+
ITestResult tr = getCurrentTestResult();
103+
tr.setThrowable(result.getError());
104+
tr.setStatus(ITestResult.SKIP);
105+
skipCount.incrementAndGet();
106+
} else if (Result.UNDEFINED.equals(result)) {
107+
ITestResult tr = getCurrentTestResult();
108+
tr.setThrowable(result.getError());
109+
tr.setStatus(ITestResult.FAILURE);
110+
failureCount.incrementAndGet();
111+
} else {
112+
passCount.incrementAndGet();
113+
}
114+
}
115+
116+
private void logResult(Result result) {
117+
String timing = computeTiming(result);
118+
119+
Step step;
120+
if (steps.isEmpty()) {
121+
step = new Step(null, "MISMATCH BETWEEN STEPS AND RESULTS", "", 0, null, null);
122+
} else {
123+
step = steps.pop();
124+
}
125+
126+
String format = "%s %s (%s%s)";
127+
String message = String.format(format, step.getKeyword(),
128+
step.getName(), result.getStatus(), timing);
129+
130+
logDiv(message, "result");
131+
}
132+
133+
private String computeTiming(Result result) {
134+
String timing = "";
135+
136+
if (result.getDuration() != null) {
137+
// TODO: Get known about the magic nature number and get rid of it.
138+
int duration = Math.round(result.getDuration() / 1000000000);
139+
timing = " : " + duration + "s";
140+
}
141+
142+
return timing;
143+
}
144+
145+
@Override
146+
public void after(Match match, Result result) {
147+
}
148+
149+
@Override
150+
public void write(String s) {
151+
}
152+
153+
@Override
154+
public void match(Match match) {
155+
}
156+
157+
@Override
158+
public void embedding(String s, byte[] bytes) {
159+
}
160+
161+
private void logDiv(String message, String cssClassName) {
162+
String format = "<div \"%s\">%s</div>";
163+
String output = String.format(format, cssClassName, message);
164+
165+
log(output);
166+
}
167+
168+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package cucumber.runtime.stub;
2+
3+
import cucumber.runtime.Backend;
4+
import cucumber.runtime.Glue;
5+
import cucumber.runtime.UnreportedStepExecutor;
6+
import cucumber.runtime.io.ResourceLoader;
7+
import gherkin.formatter.model.Step;
8+
9+
import java.util.List;
10+
11+
/**
12+
* We need an implementation of Backend to prevent Runtime from blowing up.
13+
*/
14+
public class StubBackend implements Backend {
15+
public StubBackend(ResourceLoader resourceLoader) {
16+
17+
}
18+
19+
@Override
20+
public void loadGlue(Glue glue, List<String> gluePaths) {
21+
}
22+
23+
@Override
24+
public void setUnreportedStepExecutor(UnreportedStepExecutor executor) {
25+
}
26+
27+
@Override
28+
public void buildWorld() {
29+
}
30+
31+
@Override
32+
public void disposeWorld() {
33+
}
34+
35+
@Override
36+
public String getSnippet(Step step) {
37+
return "STUB SNIPPET";
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package cucumber.runtime.testng;
2+
3+
import cucumber.api.testng.AbstractTestNGCucumberTests;
4+
5+
public class RunCukesTest extends AbstractTestNGCucumberTests {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Feature: FA
2+
3+
Scenario: SA
4+
Given GA
5+
When GA
6+
Then TA
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Feature: FB
2+
# Scenario with same step occurring twice
3+
4+
Scenario: SB
5+
When foo
6+
Then bar
7+
8+
When foo
9+
Then baz
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Feature: In cucumber.testng
2+
Scenario: first
3+
When step
4+
Then another step
5+
6+
Scenario: second
7+
When step
8+
Then another step

0 commit comments

Comments
 (0)