Crafty.Specflow.Extensions.FluentTableAsserter
1.3.1
See the version list below for details.
dotnet add package Crafty.Specflow.Extensions.FluentTableAsserter --version 1.3.1
NuGet\Install-Package Crafty.Specflow.Extensions.FluentTableAsserter -Version 1.3.1
<PackageReference Include="Crafty.Specflow.Extensions.FluentTableAsserter" Version="1.3.1" />
paket add Crafty.Specflow.Extensions.FluentTableAsserter --version 1.3.1
#r "nuget: Crafty.Specflow.Extensions.FluentTableAsserter, 1.3.1"
// Install Crafty.Specflow.Extensions.FluentTableAsserter as a Cake Addin #addin nuget:?package=Crafty.Specflow.Extensions.FluentTableAsserter&version=1.3.1 // Install Crafty.Specflow.Extensions.FluentTableAsserter as a Cake Tool #tool nuget:?package=Crafty.Specflow.Extensions.FluentTableAsserter&version=1.3.1
Specflow Fluent Table Asserter
A specflow extension library to simplify table assertion with fluent code.
Installation
dotnet add package Crafty.Specflow.Extensions.FluentTableAsserter
Why?
Asserting Specflow table can be very painful in large scale application. Even if SpecFlow.Assist Helpers is a good start to simplify data rehydration from table, it is not very flexible.
The idea to this library is:
- very little code required
- can be extended with extra configuration
- avoid creating
record
orclass
for every single table to rehydrate - tend to be ubiquitous language centric (clever string parsing from human readable input)
- make column declaration optional in gherkin, in order to declare only the columns that are
- relevant for a scenario
Examples
For the following scenario:
Scenario: List all registered customers
When I register a scientist customer "John Doe" with email address "john.doe@gmail.com"
And I register a chief product officer customer "Sam Smith" with email address "sam.smith@gmail.com"
Then the customer list is
| Name | Email address | Job |
| John Doe | john.doe@gmail.com | Scientist |
| Sam Smith | sam.smith@gmail.com | Chief product officer |
You can write the following assertion:
[Then(@"the customer list is")]
public void ThenTheCustomerListIs(Table table)
=> _customers
.ShouldBeEquivalentToTable(table)
.WithProperty(x => x.Name)
.WithProperty(x => x.EmailAddress)
.WithProperty(x => x.Job)
.Assert();
Where, for the example, Customer
is:
internal record Customer(
string Name,
string EmailAddress,
Job Job
);
public enum Job
{
Scientist,
ChiefProductOfficer
}
You can find more example here.
Mapping between columns and properties
The table asserter is smart 🤓 and try to determine column name of the table, based on the mapped property names.
EmailAddress
property is automatically mapped to EmailAddress
column or to more readable
set of words, like Email address
or email address
.
The same principle is applied for parsing enum values : ChiefProductOfficer
value works
but also Chief product officer
.
It allows to have gherkin scenario closer to natural language.
Override default comparison behaviour
In certain cases, you would want to override the column that is compared to the property.
A second argument of .WithProperty()
allows you to provide a delegate to configure the PropertyConfiguration
object.
Override column name
.WithProperty(x => x.Name, o => o.ComparedToColumn("FullName"))
It is useful when your gherkin language is different than english but your classes and records still are in english.
💡 Remember in DDD guidelines, a strong objective is to share the same language across the team / company, from domain experts to developers. The code must be aligned to domain specific terms. So in the example, if we decided a customer has a full name instead of a name, it is preferable to rename the property
Name
intoFullName
instead of overriding the mapped column.
Define conversion delegate
If the column value type (string) cannot be converted to the property type, you must define how the table asserter will converted it, by providing a conversion delegate.
.WithProperty(x => x.Price, o => o.WithColumnValueConversion(columnValue => ...)
For example, given the following Price
record.
public record Price(decimal Amount, string Symbol);
If the table looks
Scenario: Created products are listed
When I create the product "Black jacket" for 100 USD
Then the product list is
| Name | Price |
| Black jacket | $100 |
You can define the conversion as:
.WithProperty(x => x.Price, o => o.WithColumnValueConversion(columnValue => Price.Parse(columnValue))
Optional columns
All columns are optional by default, so you don't need to specify them all in all your scenarios. The ones you specify are used to assert your data. Depending on your scenario, you can specify only the ones that are relevant.
From the previous example, a new customer deletion scenario can be asserted only with the column
Name
, we can volontary remove the EmailAddress
: because it is useless here, the Name
is enough.
Scenario: Deleted customers are not listed anymore
When I delete the customer "John Doe"
Then the customer list is
| Name |
| Sam Smith |
Remaining tasks
- natively handle enumeration without converter
- handle single object assertion (instead of list)
- add more examples
- reversed converter from value to column value
- Provide default list comparison delegate converter
- handle enum flags
- provide examples on ColumnValueConversion
- provide examples on chained property expression to assert sub elements
- automatic conversion using implicit operator converter
Product | Versions 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 | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Microsoft.CodeAnalysis.CSharp (>= 4.6.0)
- SpecFlow (>= 3.9.74)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.