TickSpec 2.0.3

dotnet add package TickSpec --version 2.0.3                
NuGet\Install-Package TickSpec -Version 2.0.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TickSpec" Version="2.0.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add TickSpec --version 2.0.3                
#r "nuget: TickSpec, 2.0.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install TickSpec as a Cake Addin
#addin nuget:?package=TickSpec&version=2.0.3

// Install TickSpec as a Cake Tool
#tool nuget:?package=TickSpec&version=2.0.3                

Build Status NuGet Status Join the chat at https://gitter.im/fsprojects/TickSpec

Project Description

A lightweight Behaviour Driven Development (BDD) framework for .NET that'll fit how you want to test.

  1. Describe behaviour in plain text using the Gherkin business language, i.e. Given, When, Then.
  2. Easily execute the behaviour against matching F# 'ticked' methods, or attribute-tagged C# or F# methods.
  3. Run via your normal test runners or plugins (xUnit, NUnit or standalone)
  4. Set breakpoints in the scenarios, step definitions or your code and go (setting breakpoints in the Gherkin is currently not supported in .NET Standard version)

Example video: http://www.youtube.com/watch?v=UuTL3nj9fIE

Installation

Simply reference TickSpec via NuGet or Paket, download the assembly or build the project from source.

Feature specification (Plain text)

Feature: Refunded or replaced items should be returned to stock

Scenario: Refunded items should be returned to stock
    Given a customer buys a black jumper
    And I have 3 black jumpers left in stock
    When he returns the jumper for a refund
    Then I should have 4 black jumpers in stock

Step definitions (F#)

type StockItem = { Count : int }

let mutable stockItem = { Count = 0 }

let [<Given>] ``a customer buys a black jumper`` () = ()

let [<Given>] ``I have (.*) black jumpers left in stock`` (n:int) =
    stockItem <- { stockItem with Count = n }

let [<When>] ``he returns the jumper for a refund`` () =
    stockItem <- { stockItem with Count = stockItem.Count + 1 }

let [<Then>] ``I should have (.*) black jumpers in stock`` (n:int) =
    let passed = (stockItem.Count = n)
    Debug.Assert(passed)

Step definitions (F# without mutable field)

type StockItem = { Count : int }

let [<Given>] ``a customer buys a black jumper`` () = ()
      
let [<Given>] ``I have (.*) black jumpers left in stock`` (n:int) =
    { Count = n }
      
let [<When>] ``he returns the jumper for a refund`` (stockItem:StockItem) =
    { stockItem with Count = stockItem.Count + 1 }
      
let [<Then>] ``I should have (.*) black jumpers in stock`` (n:int) (stockItem:StockItem) =
    let passed = (stockItem.Count = n)
    Debug.Assert(passed)

Step definitions (C#)

public class StockStepDefinitions
{
   private StockItem _stockItem;

   [Given(@"a customer buys a black jumper")]
   public void GivenACustomerBuysABlackJumper()
   {
   }

   [Given(@"I have (.*) black jumpers left in stock")]
   public void GivenIHaveNBlackJumpersLeftInStock(int n)
   {
      _stockItem = new StockItem() { Count = n };
   }

   [When(@"he returns the jumper for a refund")]
   public void WhenHeReturnsTheJumperForARefund()
   {
      _stockItem.Count += 1;
   }

   [Then(@"I should have (.*) black jumpers in stock")]
   public void ThenIShouldHaveNBlackJumpersInStock(int n)
   {
      Debug.Assert(_stockItem.Count == n);
   }
}

Type Conversions

Arguments to Step Methods will be converted from string to the declared types of the Step Method parameters when possible. The following conversions are supported:

  • Enum types
  • Union types with no parameters
  • Nullable<T> types where the inner T type can be converted from string
  • Tuple types where each element can be converted from string
  • Array types T [] where T can be converted from string and the original string is comma delimited
  • Types supported by System.Convert.ChangeType

Tables

A table may be passed as an argument to a Step Method:

When a market place has outright orders:
   | Contract | Bid Qty | Bid Price | Offer Price | Offer Qty |
   | V1       | 1       | 9505      |             |           |
   | V2       |         |           | 9503        | 1         |

The parameter can be declared with type Table:

let [<When>] ``a market place has outright orders:`` (table:Table) =
    outrightOrders <- toOrders table

Alternatively, the parameter can be converted to an array of records, or other type with constructor parameters supported by the Type Conversions

type OrderRow = { Contract: string; BidQty: string; BidPrice: string; OfferPrice: string; OfferQty: string }

let [<When>] ``a market place has outright orders:`` (orderRows: OrderRow[]) =
    outrightOrders <- toOrders orderRows

The Table parameter must appear after any regex capture parameters, and before any Functional Injection parameters:

let [<Given>] ``A market place``() =
    createMarketPlace()

let [<When>] ``a market place has outright (.*) orders:``
    (orderType: string)       # captured
    (table: Table)            # table
    (maketPlace: MarketPlace) # injected
    =
  ...

Lists

A bullet list may be passed to a Step Method similarly to a Table:

Scenario: Who is the one?
Given the following actors:
    * Keanu Reeves
    * Bruce Willis
    * Johnny Depp
When the following are not available:
    * Johnny Depp
    * Bruce Willis
Then Keanu Reeves is the obvious choice

The parameter type must be an array type supported by the Type Conversions:

let [<Given>] ``the following actors:`` (actors : string[]) =
    availableActors <- Set.ofArray actors

Advanced features

Resolving referenced types (beta)

As shown in Step definitions (F# without mutable field), TickSpec also allows one to request additional parameters along with the captures from the regex holes in the step name as per typical Gherkin based frameworks. Such additional parameters can be fulfilled via the following mechanisms:

  • Instances returned from Step Method return values: This involves generating and stashing an instance in one of the preceding steps in the scenario. Typically this is achieved by returning the instance from the Step Method. Whenever a step has a return value, the the value is saved under it's type (the return type of the Step Method controls what the type is). Multiple values can be returned from a Step Method by returning a tuple. There can be only one value per type stashed per scenario run. When a parameter is being resolved, TickSpec first attempts to resolve from this type-to-instance caching Dictionary.

  • Resolving dependencies: If an instance cannot be located in the type-to-instance cache based on a preceding step having stashed the value, TickSpec will attempt to use the 'widest' constructor (the one with the most arguments) of the required type to instantiate it. Any input arguments to the constructor are all resolved recursively using the same mechanism. Any constructed instances are also cached in the type-to-instance cache, so next time it will return the same instance.

  • Accessing scenario metadata: You can access contextual information about the scenario within which a step definition is executing (e.g. tags). To do this, add a parameter of type ScenarioMetadata in the step definition argument list (or in a constructor parameter list of a dependency) scenario information (e.g. tags), then you can reference ScenarioMetadata in your method argument (or in constructor of dependencies) and you will get an instance describing the scenario which is invoked.

The lifetime of instances is per-scenario:- Each scenario run starts an empty type-to-instance cache, and at the end of the scenario the cache gets cleared. Moreover, if any instance is IDisposable, Dispose will be called.

See the example projects DependencyInjection and FunctionalInjection for typical and advanced examples of using this mechanism.

Custom type resolver (beta)

While the typical recommended usage of TickSpec is to keep the step definitions simple and drive a system from the outside in the simplest fashion possible, in some advanced cases it may be useful to provide a custom type resolver. This can be achieved by setting the StepDefinitions.ServiceProviderFactory property. This factory method is used once per scenario run to establish an independent resolution context per scenario run. The IServiceProvider instance is used to replace the built in instance construction mechanism (see Resolving dependencies in the previous section: Resolving referenced types). If the IServiceProvider implementation yielded by the factory also implements IDisposable, Dispose is called on the Service Provider context at the end of the scenario run.

See the CustomContainer example project for usage examples - the example demonstrates wiring of Autofac including usage of lifetime scopes per scenario and usage of the xUnit 2+ Shared Fixtures to correctly manage the sharing/ifetime of the container where one runs xUnit Test Classes in parallel as part of a large test suite.

Contributing

Contributions are welcome, particularly examples and documentation. If you'd like to chat about TickSpec, please use the the gitter channel.

For issues or suggestions please raise an Issue. If you are looking to extend or change the core implementation, it's best to drop a quick note and/or a placeholder PR in order to make sure there is broad agreement on the scope of the change / nature of the feature in question before investing significant time on it; we want to keep TickSpec powerful, but minimal.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net452 is compatible.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on TickSpec:

Package Downloads
TickSpec.Xunit

TickSpec integration with Xunit. TickSpec is a lightweight Behaviour Driven Development (BDD) framework for .Net. Describe behaviour in plain text using the Gherkin business language, i.e. Given, When, Then. Easily execute the behaviour against matching F# 'ticked' methods (let ``tick method`` () = true) or attributed C# or F# methods.

TickSpec.NUnit

Describe behaviour in plain text using the Gherkin business language, i.e. given, when, then. Easily execute the behaviour against matching F# tick methods (let ``tick method`` () = true) or attributed C# or F# methods.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.3 10,034 1/26/2024
2.0.2 47,037 6/19/2021
2.0.1 14,949 5/30/2021
2.0.0 8,474 4/24/2020
2.0.0-rc1 7,025 12/21/2018
1.1.0 9,230 3/14/2018
1.0.1.1 12,397 12/7/2014
1.0.0.1 3,837 11/28/2012
1.0.0 4,782 2/20/2011

[Feature] Makes `ScenarioInformation` available to step implementations through arguments resolution (issue #49)
[Fix] Performance improvements when the used assembly contains many methods (issue #45)
[Feature] Add support for functional injection in events (issue #56)
[Fix] Keep empty lines in doc strings (issue #60)
[Fix] Allow multiple step types on a single method (issue #55)
[Fix] Unit test serialization to allow running from IDE easily