├── .github └── workflows │ ├── dotnet6.yml │ ├── dotnet6noartifacts.yml │ └── dotnet7.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── RunSpecflowTests.sln ├── RunSpecflowTests ├── Features │ └── Calculator.feature ├── RunSpecflowTests.csproj └── Steps │ └── CalculatorStepDefinitions.cs └── action.yml /.github/workflows/dotnet6.yml: -------------------------------------------------------------------------------- 1 | name: .NET 6.0 2 | on: 3 | push: 4 | 5 | jobs: 6 | build: 7 | name: Build & Test 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Setup .NET 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 6.0.x 17 | - uses: ./ 18 | with: 19 | test-assembly-path: RunSpecflowTests/bin/Release/net6.0 20 | test-assembly-dll: RunSpecflowTests.dll 21 | output-html: CalculatorResults.html 22 | framework: net6.0 -------------------------------------------------------------------------------- /.github/workflows/dotnet6noartifacts.yml: -------------------------------------------------------------------------------- 1 | name: .NET 6.0 No Artifacts 2 | on: 3 | push: 4 | 5 | jobs: 6 | build: 7 | name: Build & Test 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Setup .NET 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 6.0.x 17 | - uses: ./ 18 | with: 19 | test-assembly-path: RunSpecflowTests/bin/Release/net6.0 20 | test-assembly-dll: RunSpecflowTests.dll 21 | output-html: CalculatorResults.html 22 | framework: net6.0 23 | upload-artifact: false 24 | -------------------------------------------------------------------------------- /.github/workflows/dotnet7.yml: -------------------------------------------------------------------------------- 1 | name: .NET 7.0 2 | on: 3 | push: 4 | 5 | jobs: 6 | build: 7 | name: Build & Test 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Setup .NET Core 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 7.0.x 17 | - uses: ./ 18 | with: 19 | test-assembly-dll: RunSpecflowTests/bin/Debug/net7.0/RunSpecflowTests.dll 20 | test-execution-json: RunSpecflowTests/bin/Debug/net7.0/TestExecution.json 21 | output-html: CalculatorResults.html 22 | filter: 'FullyQualifiedName~Calculator' 23 | framework: net7.0 24 | configuration: 'Debug' 25 | logger: trx 26 | logger-file-name: ../../CalculatorResults.trx 27 | - name: Publish Specflow Test Logs 28 | if: success() || failure() 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: SpecflowLogs 32 | path: | 33 | CalculatorResults.trx 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled C# 2 | */bin 3 | */obj 4 | 5 | # Compiled Specflow 6 | *.feature.cs 7 | 8 | # Visual Studio User Settings 9 | */.vs 10 | /.vs 11 | */TestResults 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 cryptic-wizard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | * A Github action to run SpecFlow tests and create a [SpecFlow+ LivingDoc](https://specflow.org/tools/living-doc/) artifact 3 | * SpecFlow projects must have a Package Reference to [SpecFlow.Plus.LivingDocPlugin](https://www.nuget.org/packages/SpecFlow.Plus.LivingDocPlugin/) in the [.csproj](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/RunSpecflowTests/RunSpecflowTests.csproj) 4 | ```Shell 5 | dotnet add package SpecFlow.Plus.LivingDocPlugin 6 | ``` 7 | ```xml 8 | 9 | ``` 10 | 11 | ## Tests 12 | [![.NET 6](https://github.com/cryptic-wizard/run-specflow-tests/actions/workflows/dotnet6.yml/badge.svg)](https://github.com/cryptic-wizard/run-specflow-tests/actions/workflows/dotnet6.yml) 13 | 14 | [![.NET 7](https://github.com/cryptic-wizard/run-specflow-tests/actions/workflows/dotnet7.yml/badge.svg)](https://github.com/cryptic-wizard/run-specflow-tests/actions/workflows/dotnet7.yml) 15 | 16 | ## Usage 17 | #### Minimal: 18 | ```yaml 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-dotnet@v4 22 | with: 23 | dotnet-version: '7.0.x' 24 | - uses: actions/cryptic-wizard/run-specflow-tests@v1.3.3 25 | with: 26 | test-assembly-path: MySpecflowProject/bin/Release/net7.0 27 | test-assembly-dll: MySpecflowProject.dll 28 | output-html: MyTestResults.html 29 | ``` 30 | 31 | #### Test Multiple Operating Systems in the Same Workflow: 32 | ```yaml 33 | jobs: 34 | build: 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | os: [ubuntu-latest, macos-latest, windows-latest] 39 | runs-on: ${{ matrix.os }} 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: actions/setup-dotnet@v4 44 | with: 45 | dotnet-version: '7.0.x' 46 | - uses: actions/cryptic-wizard/run-specflow-tests@v1.3.3 47 | with: 48 | test-assembly-path: MySpecflowProject/bin/Release/net7.0 49 | test-assembly-dll: MySpecflowProject.dll 50 | output-html: ${{ matrix.os }}.html 51 | ``` 52 | 53 | #### Test Multiple Frameworks in Separate Workflows: 54 | * Target multiple frameworks in the [.csproj](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/RunSpecflowTests/RunSpecflowTests.csproj) 55 | ```xml 56 | net6.0;net7.0 57 | ``` 58 | * [dotnet6.yml](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/.github/workflows/dotnet6.yml) 59 | * [dotnet7.yml](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/.github/workflows/dotnet7.yml) 60 | 61 | #### Optional parameters: 62 | ```yaml 63 | - uses: actions/cryptic-wizard/run-specflow-tests@v1.3.3 64 | with: 65 | test-assembly-path: MySpecflowProject/bin/Debug/net7.0 66 | test-assembly-dll: MySpecflowProject.dll 67 | test-execution-json: TestExecution.json 68 | configuration: Debug 69 | output-html: MyTestResults.html 70 | build-verbosity: normal 71 | test-verbosity: minimal 72 | filter: TestCategory=CategoryA 73 | framework: net7.0 74 | no-build: true 75 | logger: trx 76 | logger-file-name: ../../MyTestResults.trx 77 | upload-artifact: false 78 | ``` 79 | ## LivingDoc Output Example 80 | ![SpecflowLivingDoc](https://user-images.githubusercontent.com/87053379/130558124-48f01dca-a841-470d-8038-d74241fb36b2.PNG) 81 | 82 | ![SpecflowAnalytics](https://user-images.githubusercontent.com/87053379/130558132-74be6be5-8726-46a4-8c43-82daa053a603.PNG) 83 | 84 | 85 | ## Features 86 | #### Recently Added 87 | * v1.3.3 - Add filter option 88 | ```yaml 89 | filter: 90 | ``` 91 | * v1.3.2 - Patch for Github action exit code change - thanks again to awgeorge 92 | * v1.3.1 - test-execution-json now has default value 93 | ```yaml 94 | test-execution-json: 'TestExecution.json' by default 95 | ``` 96 | * v1.3.0 - Autopublish artifacts - thanks [awgeorge](https://github.com/cryptic-wizard/run-specflow-tests/commit/60ce86858a5354c70db351767d7f96cd71b6c8b1)! 97 | ```yaml 98 | upload-artifact: true by default 99 | ``` 100 | * v1.2.0 - Add configuration option 101 | ```yaml 102 | configuration: 103 | ``` 104 | * v1.1.0 - Set working folder for test-assembly-dll and test-execution-json 105 | ```yaml 106 | test-assembly-path: 107 | ``` 108 | * v1.1.0 - Allow other test loggers to be run in addition to SpecFlow 109 | ```yaml 110 | logger: 111 | logger-file-name: 112 | ``` 113 | 114 | #### Planned Features 115 | Features planned when ['uses' keyword is implemented in composite actions](https://github.com/actions/runner/issues/646) 116 | * Checkout automatically 117 | * Setup dotnet automatically 118 | * Dotnet framework matrix testing 119 | ## Tools 120 | * [Visual Studio](https://visualstudio.microsoft.com/vs/) 121 | * [NUnit 3](https://nunit.org/) 122 | * [SpecFlow](https://specflow.org/tools/specflow/) 123 | * [SpecFlow+ LivingDoc](https://specflow.org/tools/living-doc/) 124 | ## License 125 | * [MIT License](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/LICENSE.md) 126 | -------------------------------------------------------------------------------- /RunSpecflowTests.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32421.90 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RunSpecflowTests", "RunSpecflowTests\RunSpecflowTests.csproj", "{AE603549-94F5-4F84-AE14-5ABCFC58A8C9}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C7AA17EA-DC20-456A-8035-F57942C911DD}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | action.yml = action.yml 12 | .github\workflows\dotnet6.yml = .github\workflows\dotnet6.yml 13 | .github\workflows\dotnet7.yml = .github\workflows\dotnet7.yml 14 | .github\workflows\dotnet6noartifacts.yml = .github\workflows\dotnet6noartifacts.yml 15 | LICENSE.md = LICENSE.md 16 | README.md = README.md 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {AE603549-94F5-4F84-AE14-5ABCFC58A8C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {AE603549-94F5-4F84-AE14-5ABCFC58A8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {AE603549-94F5-4F84-AE14-5ABCFC58A8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {AE603549-94F5-4F84-AE14-5ABCFC58A8C9}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {A450A81D-235B-4F0E-9117-AB06E3E9CDFB} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /RunSpecflowTests/Features/Calculator.feature: -------------------------------------------------------------------------------- 1 | Feature: Calculator 2 | Simple calculator for adding **2** numbers 3 | 4 | [Calculator.feature](https://github.com/cryptic-wizard/run-specflow-tests/blob/main/RunSpecflowTests/Features/Calculator.feature) 5 | ***Further read***: **[Learn more about how to generate Living Documentation](https://docs.specflow.org/projects/specflow-livingdoc/en/latest/LivingDocGenerator/Generating-Documentation.html)** 6 | 7 | @mytag 8 | Scenario Outline: Add two numbers 9 | Given the first number is 10 | And the second number is 11 | When the two numbers are added 12 | Then the result should be 13 | 14 | Examples: 15 | | first | second | result | 16 | | 50 | 70 | 120 | 17 | | -5 | 7 | 2 | 18 | | 5 | -7 | -2 | 19 | | -5 | -7 | -12 | 20 | 21 | Scenario Outline: Subtract two numbers 22 | Given the first number is 23 | And the second number is 24 | When the two numbers are subtracted 25 | Then the result should be 26 | 27 | Examples: 28 | | first | second | result | 29 | | 50 | 70 | -20 | 30 | | -5 | 7 | -12 | 31 | | 5 | -7 | 12 | 32 | | -5 | -7 | 2 | -------------------------------------------------------------------------------- /RunSpecflowTests/RunSpecflowTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RunSpecflowTests/Steps/CalculatorStepDefinitions.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework.Legacy; 2 | using TechTalk.SpecFlow; 3 | 4 | namespace RunSpecflowSteps.Steps 5 | { 6 | [Binding] 7 | public sealed class CalculatorStepDefinitions 8 | { 9 | // For additional details on SpecFlow step definitions see https://go.specflow.org/doc-stepdef 10 | 11 | private readonly ScenarioContext _scenarioContext; 12 | 13 | public CalculatorStepDefinitions(ScenarioContext scenarioContext) 14 | { 15 | _scenarioContext = scenarioContext; 16 | } 17 | 18 | [Given("the first number is (.*)")] 19 | public void GivenTheFirstNumberIs(int number) 20 | { 21 | _scenarioContext.Add("firstNumber", number); 22 | } 23 | 24 | [Given("the second number is (.*)")] 25 | public void GivenTheSecondNumberIs(int number) 26 | { 27 | _scenarioContext.Add("secondNumber", number); 28 | } 29 | 30 | [When("the two numbers are added")] 31 | public void WhenTheTwoNumbersAreAdded() 32 | { 33 | int firstNumber = _scenarioContext.Get("firstNumber"); 34 | int secondNumber = _scenarioContext.Get("secondNumber"); 35 | ClassicAssert.IsNotNull(firstNumber); 36 | ClassicAssert.IsNotNull(secondNumber); 37 | 38 | int resultNumber = firstNumber + secondNumber; 39 | _scenarioContext.Add("resultNumber", resultNumber); 40 | } 41 | 42 | [When(@"the two numbers are subtracted")] 43 | public void WhenTheTwoNumbersAreSubtracted() 44 | { 45 | int firstNumber = _scenarioContext.Get("firstNumber"); 46 | int secondNumber = _scenarioContext.Get("secondNumber"); 47 | ClassicAssert.IsNotNull(firstNumber); 48 | ClassicAssert.IsNotNull(secondNumber); 49 | 50 | int resultNumber = firstNumber - secondNumber; 51 | _scenarioContext.Add("resultNumber", resultNumber); 52 | } 53 | 54 | 55 | [Then("the result should be (.*)")] 56 | public void ThenTheResultShouldBe(int result) 57 | { 58 | int resultNumber = _scenarioContext.Get("resultNumber"); 59 | ClassicAssert.IsNotNull(resultNumber); 60 | ClassicAssert.AreEqual(result, resultNumber); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Run SpecFlow Tests' 2 | description: 'A Github Action to run SpecFlow tests and create a LivingDoc' 3 | author: 'cryptic-wizard' 4 | branding: 5 | icon: check-square 6 | color: green 7 | 8 | inputs: 9 | test-assembly-path: 10 | description: "Path of the working directory for build assemblies (example: MySpecflowProject/bin/Debug/net7.0)" 11 | required: false 12 | default: 'null' 13 | test-assembly-dll: 14 | description: "Relative path of (example: MySpecflowProject/bin/Debug/net7.0/MySpecflowProject.dll or MySpecflowProject.dll if using test-assembly-path)" 15 | required: true 16 | test-execution-json: 17 | description: "Relative path of (example: MySpecflowProject/bin/Debug/net7.0/TestExecution.json or TestExecution.json if using test-assembly-path)" 18 | required: false 19 | default: 'TestExecution.json' 20 | output-html: 21 | description: "Name of Specflow LivingDoc output file (must end with .html) (example: MyTestResults.html)" 22 | required: false 23 | default: 'LivingDoc.html' 24 | filter: 25 | description: "Run tests that match the given description (example: TestCategory=CategoryA or FullyQualifiedName~Calculator)" 26 | required: false 27 | default: 'null' 28 | framework: 29 | description: "Framework version of dotnet (example: net6.0, net7.0)" 30 | required: false 31 | default: 'null' 32 | configuration: 33 | description: "Build configuration (example: Debug)" 34 | required: false 35 | default: 'Release' 36 | build-verbosity: 37 | description: "Verbosity of the Dotnet App Build (default = minimal)" 38 | required: false 39 | default: 'minimal' 40 | test-verbosity: 41 | description: "Verbosity of the SpecFlow Test Execution (default = normal)" 42 | required: false 43 | default: 'normal' 44 | no-build: 45 | description: "Set to true to disable dotnet build and dotnet restore" 46 | required: false 47 | default: 'false' 48 | logger: 49 | description: "Dotnet test logger to run in addition to Specflow Test Logger (example: trx)" 50 | required: false 51 | default: 'null' 52 | logger-file-name: 53 | description: "Dotnet test log file name; required if logger is defined (example: MyTestResults.trx)" 54 | required: false 55 | default: 'null' 56 | upload-artifact: 57 | description: "Set to false to disable uploading artifact automatically" 58 | required: false 59 | default: 'true' 60 | runs: 61 | using: composite 62 | steps: 63 | - name: BuildDotnetApp 64 | run: | 65 | _args=() 66 | [ "${{ inputs.configuration }}" != 'null' ] && _args+=("-c" "${{ inputs.configuration }}") 67 | [ "${{ inputs.build-verbosity }}" != 'null' ] && _args+=("-v" "${{ inputs.build-verbosity }}") 68 | [ "${{ inputs.framework }}" != 'null' ] && _args+=("-f" "${{ inputs.framework }}") 69 | 70 | [ ${{ inputs.no-build }} == 'true' ] && echo 'Build Skipped' || dotnet build "${_args[@]}" 71 | shell: bash 72 | - name: RunSpecFlowTests 73 | run: | 74 | _args=() 75 | _logger_args=() 76 | [ "${{ inputs.configuration }}" != 'null' ] && _args+=("-c" "${{ inputs.configuration }}") 77 | [ "${{ inputs.build-verbosity }}" != 'null' ] && _args+=("-v" "${{ inputs.build-verbosity }}") 78 | [ "${{ inputs.filter }}" != 'null' ] && _args+=("--filter" "${{ inputs.filter }}") 79 | [ "${{ inputs.framework }}" != 'null' ] && _args+=("-f" "${{ inputs.framework }}") 80 | [ "${{ inputs.test-verbosity }}" != 'null' ] && _logger_args+=(";verbosity=${{ inputs.test-verbosity }}") 81 | [ "${{ inputs.logger-file-name }}" != 'null' ] && _logger_args+=(";LogFileName=${{ inputs.logger-file-name }}") 82 | [ "${{ inputs.logger }}" != 'null' ] && _args+=("-l" "${{ inputs.logger }}$(IFS=; echo "${_logger_args[@]}")") 83 | 84 | if ! dotnet test --no-build "${_args[@]}"; then 85 | echo "_EXIT=1" >> $GITHUB_ENV 86 | fi 87 | shell: bash 88 | continue-on-error: true 89 | - name: GenerateLivingSpec 90 | run: | 91 | [ "${{ inputs.test-assembly-path }}" != 'null' ] && _path="${{ inputs.test-assembly-path }}" 92 | _dll="${_path:+$_path/}${{ inputs.test-assembly-dll }}" 93 | _test="${_path:+$_path/}${{ inputs.test-execution-json }}" 94 | 95 | dotnet tool install --global SpecFlow.Plus.LivingDoc.CLI 96 | livingdoc test-assembly "$_dll" -t "$_test" -o "${{ inputs.output-html }}" 97 | shell: bash 98 | - name: 'Publish Specflow Test Results' 99 | if: ${{ inputs.upload-artifact == 'true' }} 100 | uses: actions/upload-artifact@v4 101 | with: 102 | name: 'Specflow Test Results' 103 | path: ${{ inputs.output-html }} 104 | - name: ReturnCode 105 | run: | 106 | exit "${_EXIT:-0}" 107 | shell: bash 108 | --------------------------------------------------------------------------------