Skip to content

Instantly share code, notes, and snippets.

@Tiberriver256
Created December 18, 2023 17:02
Show Gist options
  • Save Tiberriver256/9ff2bf300873f70ff499255706743201 to your computer and use it in GitHub Desktop.
Save Tiberriver256/9ff2bf300873f70ff499255706743201 to your computer and use it in GitHub Desktop.
A simple console app for splitting SpecFlow feature files. The aim here is to support scenario level parallelization, which is not natively supported in SpecFlow.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Gherkin" Version="27.0.0" />
</ItemGroup>
</Project>
using Gherkin;
using Gherkin.Ast;
using System.Text;
var gherkinParser = new Parser();
var featureFiles = Directory.GetFiles(
Environment.CurrentDirectory,
"*.feature",
SearchOption.AllDirectories
);
foreach (var featureFile in featureFiles)
{
var fileInfo = new FileInfo(featureFile);
var gherkinDocument = gherkinParser.Parse(featureFile);
var background = gherkinDocument.Feature.Children.OfType<Background>().FirstOrDefault();
WriteScenarioFiles(fileInfo, gherkinDocument, background);
fileInfo.Delete();
}
static void WriteStep(StringBuilder gherkinDocumentBuilder, Step? step)
{
gherkinDocumentBuilder.AppendLine($" {step.Keyword} {step.Text}");
if (step.Argument is StepArgument argument)
{
switch (argument)
{
case DataTable dataTable:
foreach (var row in dataTable.Rows)
{
gherkinDocumentBuilder.AppendLine(
$" | {string.Join(" | ", row.Cells.Select(c => c.Value))} |"
);
}
break;
case DocString docString:
throw new NotImplementedException(
"Feature splitter does not support doc strings yet"
);
}
}
}
static void WriteScenarioFiles(
FileInfo fileInfo,
GherkinDocument gherkinDocument,
Background? background
)
{
var scenarios = gherkinDocument.Feature.Children.OfType<Scenario>();
for (var i = 0; i < scenarios.Count(); i++)
{
var scenario = scenarios.ElementAt(i);
var gherkinDocumentBuilder = new StringBuilder();
foreach (var tag in gherkinDocument.Feature.Tags)
{
gherkinDocumentBuilder.AppendLine($"@{tag.Name}");
}
gherkinDocumentBuilder.AppendLine($"");
gherkinDocumentBuilder.AppendLine($"Feature: {gherkinDocument.Feature.Name}");
gherkinDocumentBuilder.AppendLine($" {gherkinDocument.Feature.Description}");
if (background != null)
{
gherkinDocumentBuilder.AppendLine("");
gherkinDocumentBuilder.AppendLine("Background:");
foreach (var step in background.Steps)
{
WriteStep(gherkinDocumentBuilder, step);
}
}
gherkinDocumentBuilder.AppendLine("");
foreach (var tag in scenario.Tags)
{
gherkinDocumentBuilder.AppendLine($"{tag.Name}");
}
gherkinDocumentBuilder.AppendLine($"{scenario.Keyword}: {scenario.Name}");
if (scenario.Description != null)
{
gherkinDocumentBuilder.AppendLine($" {scenario.Description}");
}
foreach (var step in scenario.Steps)
{
WriteStep(gherkinDocumentBuilder, step);
}
if (scenario.Examples != null)
{
foreach (var example in scenario.Examples)
{
gherkinDocumentBuilder.AppendLine("");
foreach (var tag in example.Tags)
{
gherkinDocumentBuilder.AppendLine($" {tag.Name}");
}
gherkinDocumentBuilder.AppendLine($" Examples: {example.Name}");
gherkinDocumentBuilder.AppendLine(
$" | {string.Join(" | ", example.TableHeader.Cells.Select(c => c.Value))} |"
);
foreach (var row in example.TableBody)
{
gherkinDocumentBuilder.AppendLine(
$" | {string.Join(" | ", row.Cells.Select(c => c.Value))} |"
);
}
}
}
var newFolder = Path.Combine(fileInfo.DirectoryName!, scenario.Name);
if (!Directory.Exists(newFolder))
{
Directory.CreateDirectory(newFolder);
}
var newFeatureFile = Path.Combine(
newFolder,
$"{fileInfo.Name.Replace(".feature", "")}.{scenario.Name}.feature"
);
using (var writer = new StreamWriter(newFeatureFile))
{
writer.Write(gherkinDocumentBuilder);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment