Skip to content

Commit 8554d3b

Browse files
daantimmerBartKerkhoffCopilot
authored
feat: add option to print steps that have not been used (#227)
Co-authored-by: Bart Kerkhoff <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c4ef094 commit 8554d3b

File tree

6 files changed

+90
-3
lines changed

6 files changed

+90
-3
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@unused_steps
2+
Feature: Test for unused step detection
3+
Scenario: One step used, one step not used
4+
When this step is being used
5+
# And this step is not being used

cucumber_cpp/acceptance_test/steps/Steps.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ GIVEN("Next block of text enclosed in \"\"\" characters")
6363
ASSERT_THAT(docString, testing::Eq("Multiline\nDocstring"));
6464
}
6565

66+
WHEN("this step is being used")
67+
{
68+
// empty
69+
}
70+
71+
WHEN("this step is not being used")
72+
{
73+
// empty
74+
}
75+
6676
WHEN("I throw an exception")
6777
{
6878
throw std::runtime_error{ "Exception thrown" };

cucumber_cpp/acceptance_test/test.bats

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,16 @@ teardown() {
192192
assert_success
193193
assert_output --partial "tests : 1/1 passed"
194194
}
195+
196+
@test "Test unused step reporting" {
197+
run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@unused_steps" --report console --unused
198+
assert_success
199+
assert_output --regexp ".*The following steps have not been used:.*this step is not being used.*"
200+
refute_output --regexp ".*The following steps have not been used:.*this step is being used.*"
201+
}
202+
203+
@test "Test unused steps by default not reported" {
204+
run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@unused_steps" --report console
205+
assert_success
206+
refute_output --partial "The following steps have not been used:"
207+
}

cucumber_cpp/library/Application.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "cucumber_cpp/library/Errors.hpp"
44
#include "cucumber_cpp/library/StepRegistry.hpp"
55
#include "cucumber_cpp/library/cucumber_expression/Errors.hpp"
6+
#include "cucumber_cpp/library/cucumber_expression/Matcher.hpp"
67
#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp"
78
#include "cucumber_cpp/library/engine/ContextManager.hpp"
89
#include "cucumber_cpp/library/engine/FeatureFactory.hpp"
@@ -34,6 +35,7 @@
3435
#include <string_view>
3536
#include <type_traits>
3637
#include <utility>
38+
#include <variant>
3739
#include <vector>
3840

3941
namespace cucumber_cpp::library
@@ -115,6 +117,7 @@ namespace cucumber_cpp::library
115117
runCommand->add_option("--outputfolder", options.outputfolder, "Specifies the output folder for generated report files")->group("report generation");
116118
runCommand->add_option("--reportfile", options.reportfile, "Specifies the output name for generated report files")->group("report generation");
117119
runCommand->add_flag("--dry", options.dryrun, "Generate report without running tests");
120+
runCommand->add_flag("--unused", options.printStepsNotUsed, "Show step definitions that were not used");
118121

119122
reporters.Add("console", std::make_unique<report::StdOutReport>());
120123
reporters.Add("junit-xml", std::make_unique<report::JunitReport>(options.outputfolder, options.reportfile));
@@ -213,10 +216,32 @@ namespace cucumber_cpp::library
213216

214217
testRunner.Run(GetFeatureTree(featureTreeFactory, tagExpression));
215218

219+
if (options.printStepsNotUsed)
220+
PrintStepsNotUsed(stepRegistry);
221+
216222
std::cout << '\n'
217223
<< std::flush;
218224
}
219225

226+
void Application::PrintStepsNotUsed(const StepRegistry& stepRegistry) const
227+
{
228+
auto isUnused = [](const StepRegistry::EntryView& entry)
229+
{
230+
return entry.used == 0;
231+
};
232+
233+
auto unusedSteps = stepRegistry.List() | std::views::filter(isUnused);
234+
235+
if (std::ranges::empty(unusedSteps))
236+
std::cout << "\nAll steps have been used.";
237+
else
238+
{
239+
std::cout << "\nThe following steps have not been used:";
240+
for (const auto& entry : unusedSteps)
241+
std::cout << "\n - " << std::visit(cucumber_expression::SourceVisitor{}, entry.stepRegex);
242+
}
243+
}
244+
220245
std::vector<std::unique_ptr<engine::FeatureInfo>> Application::GetFeatureTree(const engine::FeatureTreeFactory& featureTreeFactory, std::string_view tagExpression)
221246
{
222247

cucumber_cpp/library/Application.hpp

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

77
#include "cucumber/gherkin/app.hpp"
88
#include "cucumber_cpp/library/Context.hpp"
9+
#include "cucumber_cpp/library/StepRegistry.hpp"
910
#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp"
1011
#include "cucumber_cpp/library/engine/ContextManager.hpp"
1112
#include "cucumber_cpp/library/engine/FeatureFactory.hpp"
@@ -40,6 +41,7 @@ namespace cucumber_cpp::library
4041
std::string reportfile{ "TestReport" };
4142

4243
bool dryrun{ false };
44+
bool printStepsNotUsed{ false };
4345
};
4446

4547
explicit Application(std::shared_ptr<ContextStorageFactory> contextStorageFactory = std::make_shared<ContextStorageFactoryImpl>(), bool removeDefaultGoogleTestListener = true);
@@ -53,12 +55,14 @@ namespace cucumber_cpp::library
5355
void AddReportHandler(const std::string& name, std::unique_ptr<report::ReportHandlerV2>&& reporter);
5456

5557
private:
56-
[[nodiscard]] int GetExitCode() const;
57-
[[nodiscard]] int GetExitCode(engine::Result result) const;
5858
void DryRunFeatures();
5959
void RunFeatures();
60-
[[nodiscard]] std::vector<std::unique_ptr<engine::FeatureInfo>> GetFeatureTree(const engine::FeatureTreeFactory& featureTreeFactory, std::string_view tagExpression);
6160
[[nodiscard]] engine::Result RunFeature(const std::filesystem::path& path, std::string_view tagExpression, report::ReportHandlerV2& reportHandler);
61+
void PrintStepsNotUsed(const StepRegistry& stepRegistry) const;
62+
[[nodiscard]] std::vector<std::unique_ptr<engine::FeatureInfo>> GetFeatureTree(const engine::FeatureTreeFactory& featureTreeFactory, std::string_view tagExpression);
63+
64+
[[nodiscard]] int GetExitCode() const;
65+
[[nodiscard]] int GetExitCode(engine::Result result) const;
6266

6367
Options options;
6468
CLI::App cli;

cucumber_cpp/library/test/TestApplication.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cucumber_cpp/CucumberCpp.hpp"
22
#include "cucumber_cpp/library/Application.hpp"
33
#include "cucumber_cpp/library/engine/test_helper/TemporaryFile.hpp"
4+
#include "gmock/gmock.h"
45
#include <CLI/Error.hpp>
56
#include <array>
67
#include <cstddef>
@@ -110,4 +111,33 @@ namespace cucumber_cpp::library
110111
{
111112
EXPECT_THAT(&Application{}.ParameterRegistration(), testing::NotNull());
112113
}
114+
115+
TEST_F(TestApplication, UnusedParameters)
116+
{
117+
auto tmp = engine::test_helper::TemporaryFile{ "tmpfile.feature" };
118+
const auto path = tmp.Path().string();
119+
120+
tmp << "Feature: Test feature\n"
121+
" Rule: Test rule\n"
122+
" Scenario: Test scenario1\n"
123+
" Given 5 and 5 are equal\n"
124+
" And This is a GIVEN step\n"
125+
" And This is a WHEN step\n"
126+
" And This is a THEN step\n"
127+
" And This is a STEP step\n";
128+
129+
const std::array args{ "application", "run", "--feature", path.c_str(), "--report", "console", "--unused" };
130+
131+
std::string stdoutString = RunWithArgs(args, static_cast<std::underlying_type_t<CLI::ExitCodes>>(CLI::ExitCodes::Success));
132+
133+
EXPECT_THAT(stdoutString, testing::HasSubstr("The following steps have not been used:"));
134+
EXPECT_THAT(stdoutString, testing::HasSubstr("^This is a step with a ([0-9]+)s delay$"));
135+
EXPECT_THAT(stdoutString, testing::HasSubstr("Step with cucumber expression syntax {float} {string} {int}"));
136+
137+
EXPECT_THAT(stdoutString, testing::Not(testing::HasSubstr("{int} and {int} are equal")));
138+
EXPECT_THAT(stdoutString, testing::Not(testing::HasSubstr("And This is a GIVEN step")));
139+
EXPECT_THAT(stdoutString, testing::Not(testing::HasSubstr("And This is a WHEN step")));
140+
EXPECT_THAT(stdoutString, testing::Not(testing::HasSubstr("And This is a THEN step")));
141+
EXPECT_THAT(stdoutString, testing::Not(testing::HasSubstr("And This is a STEP step")));
142+
}
113143
}

0 commit comments

Comments
 (0)