-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGherkinCustomFailureHandler.cpp
More file actions
82 lines (73 loc) · 3.5 KB
/
Copy pathGherkinCustomFailureHandler.cpp
File metadata and controls
82 lines (73 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <BabyBehave/bdd.hpp>
#include <iostream>
#include <vector>
using namespace BabyBehave::BDD;
using namespace BabyBehave::BDD::Gherkin;
// Demonstrates the v0.8.1 onFailureCallback extension point on
// Feature(...).OnFailure(...).
//
// By default a Feature run fails hard: any parse error or failing Scenario
// prints a diagnostic and calls std::exit(EXIT_FAILURE) immediately (see
// GherkinUnmatchedStep.cpp/GherkinCollectFailures.cpp). That's the right
// default for a small standalone binary, but an advanced consumer plugging
// Gherkin into a larger test harness (their own reporter, CI aggregation,
// etc.) may want to redirect those failures instead of crashing the process
// outright. Passing Gherkin::CollectingFailureHandler as OnFailure(...)
// does exactly that.
//
// A GherkinFailureCallback (std::function<void(std::string_view)>, not
// move_only_function) may be invoked more than once per Feature run - once
// for a parse error, or once per failing Scenario - so CollectingFailureHandler
// collects every message into a std::vector<std::string> rather than
// assuming a single call.
//
// Because our callback here does NOT exit or throw, the Feature run keeps
// going after a failing Scenario instead of stopping at the first one (this
// is the "collect Gherkin failures across the whole feature" mode the
// design doc describes) and returns a FeatureResult with allPassed==false
// once every Scenario has run. This example inspects that at the end and
// decides its own process exit code, rather than letting the run decide
// for it.
int main() {
StepRegistry registry;
registry.RegisterGiven("a counter starting at {int}", [](TestContext& ctx, int start) -> bool {
ctx.Set("counter", start);
return true;
});
registry.RegisterWhen("I increment the counter by {int}", [](TestContext& ctx, int delta) -> bool {
ctx.Mutate<int>("counter") += delta;
return true;
});
registry.RegisterThen("the counter should be {int}", [](TestContext& ctx, int expected) -> bool {
return ctx.Get<int>("counter") == expected;
});
const std::string_view feature = R"feature(
Feature: Custom failure handling
Scenario: A passing scenario
Given a counter starting at 1
When I increment the counter by 2
Then the counter should be 3
Scenario: A failing scenario
Given a counter starting at 1
When I increment the counter by 2
Then the counter should be 100
)feature";
// Collect every Gherkin-sourced failure message instead of letting
// the run print-and-exit for us.
std::vector<std::string> collectedFailures;
const CollectingFailureHandler collectFailures(collectedFailures);
const auto result = Feature(std::string(feature), registry)
.Label("examples/GherkinCustomFailureHandler.cpp")
.OnFailure(collectFailures)
.Run();
std::cout << "Scenarios run: " << result.scenarioResults.size() << '\n';
std::cout << "Failures collected by our custom callback: " << collectedFailures.size() << '\n';
for (const auto& message : collectedFailures) {
std::cout << " - " << message << '\n';
}
// The consumer decides the process's fate, not the builder itself: here
// we mirror the library's own fail-hard convention by exiting non-zero
// when any Scenario failed, but a real harness could just as easily log
// this and keep going (e.g. to run more features before reporting).
return result.ExitCode();
}