fsprojects / TickSpec

Lean .NET BDD framework with powerful F# integration
Apache License 2.0
133 stars 23 forks source link

Allow Tables and Functional Injection to be used together #32

Closed mbuhot closed 5 years ago

mbuhot commented 5 years ago

This PR addresses an issue where Table parameters cannot be used along side Functional Injection in the same step. The existing code assumed that the Table would always be the last argument, while also assuming that the trailing arguments would be injected.

This change puts the Table argument after the regex captures, leaving the trailing arguments to be injected.

To test this change I modified the Shopping.feature and ShoppingSteps.fs files:

Feature: Shopping

Scenario: Shopping in a store
Given I have 100 dollars in my wallet
When I buy items:
  |Description|Amount|
  |Popcorn    |5     |
  |Tickets    |10    |
Then My wallet contains 85 dollars
type Wallet = WalletDollars of int

let [<Given>] ``I have (.*) dollars in my wallet`` (amount:int) =
    WalletDollars amount // The output of function gets stored by type

// Extra parameters are restored from store by its type, in this case it is the wallet
let [<When>] ``I buy items:`` (items: Table) (WalletDollars balance) =
    let total =
        items.Rows
        |> Seq.map(fun r -> r.[1])
        |> Seq.sumBy(Int32.Parse)

    // Wallet value is replaced in store thanks to this output
    WalletDollars (balance - total)

let [<Then>] ``My wallet contains (.*) dollars`` (amount:int) (WalletDollars wallet) =
    Assert.AreEqual(amount, wallet)

Before this commit, the tests would fail with:

Users/mbuhot/src/TickSpec> "/usr/local/share/dotnet/dotnet" test /Users/mbuhot/src/TickSpec/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj --configuration Release --no-build /bl:/var/folders/dz/7m2pdfrs66v5qqgnwcwl_sgr0000gn/T/tmpSJ3ltv.tmp.binlog (In: false, Out: false, Err: false)
/usr/local/share/dotnet/sdk/2.1.403/MSBuild.dll -nologo -distributedlogger:Microsoft.DotNet.Tools.MSBuild.MSBuildLogger,/usr/local/share/dotnet/sdk/2.1.403/dotnet.dll*Microsoft.DotNet.Tools.MSBuild.MSBuildForwardingLogger,/usr/local/share/dotnet/sdk/2.1.403/dotnet.dll -maxcpucount -nodereuse:false -property:Configuration=Release -property:VSTestNoBuild=true -target:VSTest -verbosity:m -verbosity:quiet /bl:/var/folders/dz/7m2pdfrs66v5qqgnwcwl_sgr0000gn/T/tmpSJ3ltv.tmp.binlog /Users/mbuhot/src/TickSpec/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj
Test run for /Users/mbuhot/src/TickSpec/Examples/ByFeature/FunctionalInjection/bin/Release/netcoreapp2.1/FunctionalInjection.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.8.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
Failed   Scenario: Shopping in a store
Error Message:
 System.Exception : Expecting table argument
Stack Trace:
   at TickSpec.ScenarioRun.invokeStep(IDictionary`2 parsers, IInstanceProvider provider, MethodInfo meth, String[] args, FSharpOption`1 bullets, FSharpOption`1 table, FSharpOption`1 doc) in /Users/mbuhot/src/TickSpec/TickSpec/ScenarioRun.fs:line 121
   at TickSpec.ScenarioRun.generate@174.Invoke(Tuple`3 tupledArg) in /Users/mbuhot/src/TickSpec/TickSpec/ScenarioRun.fs:line 177
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source)
   at TickSpec.ScenarioRun.generate[a,b](IEnumerable`1 events_0, IEnumerable`1 events_1, IEnumerable`1 events_2, IEnumerable`1 events_3, IDictionary`2 parsers, a scenario, IEnumerable`1 lines, FSharpRef`1 serviceProviderFactory, Unit unitVar0) in /Users/mbuhot/src/TickSpec/TickSpec/ScenarioRun.fs:line 174
   at <StartupCode$TickSpec>.$TickSpec.action@185.Invoke(Unit arg40@) in /Users/mbuhot/src/TickSpec/TickSpec/TickSpec.fs:line 185
   at <StartupCode$TickSpec>.$TickSpec.GenerateScenarios@187-1.Invoke() in /Users/mbuhot/src/TickSpec/TickSpec/TickSpec.fs:line 187
   at NUnit.TickSpec.FeatureFixture.Bdd(Scenario scenario) in /Users/mbuhot/src/TickSpec/Examples/ByFeature/FunctionalInjection/NunitWiring.fs:line 16

Total tests: 6. Passed: 5. Failed: 1. Skipped: 0.
Test Run Failed.
Test execution time: 1.3179 Seconds
Test run for /Users/mbuhot/src/TickSpec/Examples/ByFeature/FunctionalInjection/bin/Release/net452/FunctionalInjection.dll(.NETFramework,Version=v4.5.2)
Microsoft (R) Test Execution Command Line Tool Version 15.8.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
Failed   Scenario: Shopping in a store
Error Message:
 System.ArgumentException : Expecting table or array argument
Parameter name: _arg1
Stack Trace:
  at Scenario: Shopping in a store.5: When I buy items: () [0x00000] in <3c9cff5a8252404ebbeab30a1d324eed>:0
  at Scenario: Shopping in a store.Run () [0x0000e] in <3c9cff5a8252404ebbeab30a1d324eed>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x0003b] in <1010bb418d91488f8e6cc9deec6352ef>:0
--- End of stack trace from previous location where exception was thrown ---
  at NUnit.TickSpec+FeatureFixture.Bdd (TickSpec.Scenario scenario) [0x0005b] in <5c0dbae2291ad172a7450383e2ba0d5c>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x0003b] in <1010bb418d91488f8e6cc9deec6352ef>:0

Total tests: 6. Passed: 5. Failed: 1. Skipped: 0.
Test Run Failed.
Test execution time: 1.4359 Seconds
Finished (Failed) 'DotNet:test' in 00:00:04.3213087
Finished (Failed) 'Test' in 00:00:19.1190369
bartelink commented 5 years ago

This writeup is exemplary and the work looks great too (I'm not familiar with what the code area you've changed so will make no comment regarding wether it looks safe or correct beyond lgtm). Seems to me your test should become a new sample, a new readme.md entry or both - AFAIR there are few Table examples, and this example in particular shows stuff that's pretty much a new usage paradigm beyond what's in Phil's blogs etc. I'm not pushing for any particular direction, but to not make this part of block of work while you have it top of mind would seem like a lost opportunity.

mbuhot commented 5 years ago

Thanks @bartelink I've added some documentation to the README describing how Type Conversions, Tables and Lists work, based on the source of ScenarioRun.fs.

bartelink commented 5 years ago

:love: That's fantastic! @mchaloupka any last minute things before this merges ?

mbuhot commented 5 years ago

Thanks @bartelink, I've incorporated some of the suggestions.

mbuhot commented 5 years ago

@mchaloupka Any further changes required before this PR can be merged?

mchaloupka commented 5 years ago

No additional comments. If there will be no additional comment today, I will merge it.

Thanks for your work!