Skip to content

Commit e086185

Browse files
author
Jake Ginnivan
committed
First attempt at async version of fluent API
1 parent fd2a856 commit e086185

File tree

9 files changed

+212
-4
lines changed

9 files changed

+212
-4
lines changed

TestStack.BDDfy.Tests/Scanner/WhenStepScannerFactoryAsyncMethods.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ namespace TestStack.BDDfy.Tests.Scanner
1010
public class WhenStepScannerFactoryAsyncMethods
1111
{
1212
[Test]
13-
[Ignore("This requires overloads to all the fluent methods to capture the task")]
1413
public void CallingAsyncTaskWhichThrowsIsObservedAndRethrown()
1514
{
1615
var stepAction = StepActionFactory.GetStepAction<SomeScenario>(o => AsyncTaskMethod(o));
@@ -21,7 +20,7 @@ public void CallingAsyncTaskWhichThrowsIsObservedAndRethrown()
2120
[Test]
2221
public void CallingAsyncVoidWhichThrowsIsObservedAndRethrown()
2322
{
24-
var stepAction = StepActionFactory.GetStepAction<SomeScenario>(AsyncVoidMethod);
23+
var stepAction = StepActionFactory.GetStepAction<SomeScenario>(s=>AsyncVoidMethod(s));
2524

2625
Assert.Throws<ArgumentException>(() => stepAction(new SomeScenario()));
2726
}

TestStack.BDDfy/Scanners/StepScanners/Fluent/ExpressionExtensions.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ public static IEnumerable<object> ExtractConstants<T>(this Expression<Action<T>>
2222
return ExtractConstants(methodCallExpression);
2323
}
2424

25+
#if !NET35
26+
public static IEnumerable<object> ExtractConstants<T>(this Expression<Func<T, System.Threading.Tasks.Task>> expression)
27+
{
28+
var lambdaExpression = expression as LambdaExpression;
29+
if (lambdaExpression == null)
30+
throw new InvalidOperationException("Please provide a lambda expression.");
31+
32+
var methodCallExpression = lambdaExpression.Body as MethodCallExpression;
33+
if (methodCallExpression == null)
34+
throw new InvalidOperationException("Please provide a *method call* lambda expression.");
35+
36+
return ExtractConstants(methodCallExpression);
37+
}
38+
#endif
39+
2540
private static IEnumerable<object> ExtractConstants(Expression expression)
2641
{
2742
if (expression == null || expression is ParameterExpression)
@@ -146,7 +161,7 @@ private static IEnumerable<object> ExtractConstants(ConstantExpression constantE
146161
else
147162
{
148163
if (constantExpression.Type == typeof(string) ||
149-
constantExpression.Type == typeof(decimal) ||
164+
constantExpression.Type == typeof(decimal) ||
150165
constantExpression.Type.IsPrimitive ||
151166
constantExpression.Type.IsEnum ||
152167
constantExpression.Value == null)

TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentScanner.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
using TestStack.BDDfy.Core;
77
using TestStack.BDDfy.Scanners.ScenarioScanners;
88

9+
#if !NET35
10+
using System.Threading.Tasks;
11+
#endif
12+
913
namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
1014
{
1115
/// <summary>
@@ -103,6 +107,139 @@ IAndGiven<TScenario> IGiven<TScenario>.And(Expression<Action<TScenario>> andGive
103107
{
104108
return ((IGiven<TScenario>)this).And(andGivenStep, null);
105109
}
110+
#else
111+
IGiven<TScenario> IInitialStep<TScenario>.Given(Expression<Func<TScenario, Task>> givenStep, string stepTextTemplate)
112+
{
113+
AddStep(givenStep, stepTextTemplate, false, ExecutionOrder.SetupState);
114+
return this;
115+
}
116+
117+
IWhen<TScenario> IInitialStep<TScenario>.When(Expression<Func<TScenario, Task>> whenStep, string stepTextTemplate)
118+
{
119+
AddStep(whenStep, stepTextTemplate, false, ExecutionOrder.Transition);
120+
return this;
121+
}
122+
123+
IGiven<TScenario> IInitialStep<TScenario>.Given(Expression<Func<TScenario, Task>> givenStep, bool includeInputsInStepTitle)
124+
{
125+
AddStep(givenStep, null, false, ExecutionOrder.SetupState, includeInputsInStepTitle: includeInputsInStepTitle);
126+
return this;
127+
}
128+
129+
IWhen<TScenario> IInitialStep<TScenario>.When(Expression<Func<TScenario, Task>> whenStep, bool includeInputsInStepTitle)
130+
{
131+
AddStep(whenStep, null, false, ExecutionOrder.Transition, includeInputsInStepTitle: includeInputsInStepTitle);
132+
return this;
133+
}
134+
135+
IThen<TScenario> IWhen<TScenario>.Then(Expression<Func<TScenario, Task>> thenStep, string stepTextTemplate)
136+
{
137+
AddStep(thenStep, stepTextTemplate, true, ExecutionOrder.Assertion);
138+
return this;
139+
}
140+
141+
IAndThen<TScenario> IThen<TScenario>.And(Expression<Func<TScenario, Task>> andThenStep, string stepTextTemplate)
142+
{
143+
AddStep(andThenStep, stepTextTemplate, true, ExecutionOrder.ConsecutiveAssertion);
144+
return this;
145+
}
146+
147+
IAndThen<TScenario> IThen<TScenario>.And(Expression<Func<TScenario, Task>> andThenStep, bool includeInputsInStepTitle)
148+
{
149+
AddStep(andThenStep, null, true, ExecutionOrder.ConsecutiveAssertion, includeInputsInStepTitle: includeInputsInStepTitle);
150+
return this;
151+
}
152+
153+
IAndWhen<TScenario> IWhen<TScenario>.And(Expression<Func<TScenario, Task>> andWhenStep, bool includeInputsInStepTitle)
154+
{
155+
AddStep(andWhenStep, null, false, ExecutionOrder.ConsecutiveTransition, includeInputsInStepTitle: includeInputsInStepTitle);
156+
return this;
157+
}
158+
159+
IThen<TScenario> IWhen<TScenario>.Then(Expression<Func<TScenario, Task>> thenStep, bool includeInputsInStepTitle)
160+
{
161+
AddStep(thenStep, null, true, ExecutionOrder.Assertion, includeInputsInStepTitle: includeInputsInStepTitle);
162+
return this;
163+
}
164+
165+
IAndGiven<TScenario> IGiven<TScenario>.And(Expression<Func<TScenario, Task>> andGivenStep, bool includeInputsInStepTitle)
166+
{
167+
AddStep(andGivenStep, null, false, ExecutionOrder.ConsecutiveSetupState, includeInputsInStepTitle: includeInputsInStepTitle);
168+
return this;
169+
}
170+
171+
IThen<TScenario> IGiven<TScenario>.Then(Expression<Func<TScenario, Task>> thenStep, bool includeInputsInStepTitle)
172+
{
173+
AddStep(thenStep, null, true, ExecutionOrder.Assertion, includeInputsInStepTitle: includeInputsInStepTitle);
174+
return this;
175+
}
176+
177+
IAndGiven<TScenario> IGiven<TScenario>.And(Expression<Func<TScenario, Task>> andGivenStep, string stepTextTemplate)
178+
{
179+
AddStep(andGivenStep, stepTextTemplate, false, ExecutionOrder.ConsecutiveSetupState);
180+
return this;
181+
}
182+
183+
IAndWhen<TScenario> IWhen<TScenario>.And(Expression<Func<TScenario, Task>> andWhenStep, string stepTextTemplate)
184+
{
185+
AddStep(andWhenStep, stepTextTemplate, false, ExecutionOrder.ConsecutiveTransition);
186+
return this;
187+
}
188+
189+
IThen<TScenario> IGiven<TScenario>.Then(Expression<Func<TScenario, Task>> thenStep, string stepTextTemplate)
190+
{
191+
AddStep(thenStep, stepTextTemplate, true, ExecutionOrder.Assertion);
192+
return this;
193+
}
194+
195+
IWhen<TScenario> IGiven<TScenario>.When(Expression<Func<TScenario, Task>> whenStep, bool includeInputsInStepTitle)
196+
{
197+
AddStep(whenStep, null, false, ExecutionOrder.Transition, includeInputsInStepTitle: includeInputsInStepTitle);
198+
return this;
199+
}
200+
201+
IWhen<TScenario> IGiven<TScenario>.When(Expression<Func<TScenario, Task>> whenStep, string stepTextTemplate)
202+
{
203+
AddStep(whenStep, stepTextTemplate, false, ExecutionOrder.Transition);
204+
return this;
205+
}
206+
207+
IFluentScanner<TScenario> IFluentScanner<TScenario>.TearDownWith(Expression<Func<TScenario, Task>> tearDownStep)
208+
{
209+
AddStep(tearDownStep, null, false, ExecutionOrder.TearDown, false);
210+
return this;
211+
}
212+
213+
private void AddStep(Expression<Func<TScenario, Task>> stepAction, string stepTextTemplate, bool asserts, ExecutionOrder executionOrder, bool reports = true, bool includeInputsInStepTitle = true)
214+
{
215+
var methodInfo = GetMethodInfo(stepAction);
216+
var inputArguments = new object[0];
217+
if (includeInputsInStepTitle)
218+
{
219+
inputArguments = stepAction.ExtractConstants().ToArray();
220+
}
221+
222+
var flatInputArray = inputArguments.FlattenArrays();
223+
var stepTitle = NetToString.Convert(methodInfo.Name);
224+
225+
if (!string.IsNullOrEmpty(stepTextTemplate))
226+
stepTitle = string.Format(stepTextTemplate, flatInputArray);
227+
else if (includeInputsInStepTitle)
228+
{
229+
var stringFlatInputs = flatInputArray.Select(i => i.ToString()).ToArray();
230+
stepTitle = stepTitle + " " + string.Join(", ", stringFlatInputs);
231+
}
232+
233+
stepTitle = stepTitle.Trim();
234+
var action = stepAction.Compile();
235+
_steps.Add(new ExecutionStep(StepActionFactory.GetStepAction(action), stepTitle, asserts, executionOrder, reports));
236+
}
237+
238+
private static MethodInfo GetMethodInfo(Expression<Func<TScenario, Task>> stepAction)
239+
{
240+
var methodCall = (MethodCallExpression)stepAction.Body;
241+
return methodCall.Method;
242+
}
106243
#endif
107244

108245
private void AddStep(Expression<Action<TScenario>> stepAction, string stepTextTemplate, bool asserts, ExecutionOrder executionOrder, bool reports = true, bool includeInputsInStepTitle = true)

TestStack.BDDfy/Scanners/StepScanners/Fluent/IFluentScanner.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
77
public interface IFluentScanner<TScenario> : IHasScanner
88
{
99
IFluentScanner<TScenario> TearDownWith(Expression<Action<TScenario>> tearDownStep);
10+
#if !NET35
11+
IFluentScanner<TScenario> TearDownWith(Expression<Func<TScenario, System.Threading.Tasks.Task>> tearDownStep);
12+
#endif
1013
}
1114
}

TestStack.BDDfy/Scanners/StepScanners/Fluent/IGiven.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System;
22
using System.Linq.Expressions;
33

4+
#if !NET35
5+
using System.Threading.Tasks;
6+
#endif
7+
48
namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
59
{
610
public interface IGiven<TScenario>
@@ -20,6 +24,14 @@ public interface IGiven<TScenario>
2024
IWhen<TScenario> When(Expression<Action<TScenario>> whenStep, string stepTextTemplate = null);
2125
IAndGiven<TScenario> And(Expression<Action<TScenario>> andGivenStep, string stepTextTemplate = null);
2226
IThen<TScenario> Then(Expression<Action<TScenario>> thenStep, string stepTextTemplate = null);
27+
28+
IWhen<TScenario> When(Expression<Func<TScenario, Task>> whenStep, string stepTextTemplate = null);
29+
IAndGiven<TScenario> And(Expression<Func<TScenario, Task>> andGivenStep, string stepTextTemplate = null);
30+
IThen<TScenario> Then(Expression<Func<TScenario, Task>> thenStep, string stepTextTemplate = null);
31+
32+
IWhen<TScenario> When(Expression<Func<TScenario, Task>> whenStep, bool includeInputsInStepTitle);
33+
IAndGiven<TScenario> And(Expression<Func<TScenario, Task>> andGivenStep, bool includeInputsInStepTitle);
34+
IThen<TScenario> Then(Expression<Func<TScenario, Task>> thenStep, bool includeInputsInStepTitle);
2335
#endif
2436
}
2537
}

TestStack.BDDfy/Scanners/StepScanners/Fluent/IInitialStep.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System;
22
using System.Linq.Expressions;
33

4+
#if !NET35
5+
using System.Threading.Tasks;
6+
#endif
7+
48
namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
59
{
610
public interface IInitialStep<TScenario>
@@ -16,6 +20,11 @@ public interface IInitialStep<TScenario>
1620
#else
1721
IGiven<TScenario> Given(Expression<Action<TScenario>> givenStep, string stepTextTemplate = null);
1822
IWhen<TScenario> When(Expression<Action<TScenario>> whenStep, string stepTextTemplate = null);
23+
24+
IGiven<TScenario> Given(Expression<Func<TScenario, Task>> givenStep, string stepTextTemplate = null);
25+
IWhen<TScenario> When(Expression<Func<TScenario, Task>> whenStep, string stepTextTemplate = null);
26+
IGiven<TScenario> Given(Expression<Func<TScenario, Task>> givenStep, bool includeInputsInStepTitle);
27+
IWhen<TScenario> When(Expression<Func<TScenario, Task>> whenStep, bool includeInputsInStepTitle);
1928
#endif
2029
}
2130
}

TestStack.BDDfy/Scanners/StepScanners/Fluent/IThen.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System;
22
using System.Linq.Expressions;
33

4+
#if !NET35
5+
using System.Threading.Tasks;
6+
#endif
7+
48
namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
59
{
610
public interface IThen<TScenario> : IFluentScanner<TScenario>
@@ -11,6 +15,8 @@ public interface IThen<TScenario> : IFluentScanner<TScenario>
1115
IAndThen<TScenario> And(Expression<Action<TScenario>> andThenStep, string stepTextTemplate);
1216
#else
1317
IAndThen<TScenario> And(Expression<Action<TScenario>> andThenStep, string stepTextTemplate = null);
18+
IAndThen<TScenario> And(Expression<Func<TScenario, Task>> andThenStep, string stepTextTemplate = null);
19+
IAndThen<TScenario> And(Expression<Func<TScenario, Task>> andThenStep, bool includeInputsInStepTitle);
1420
#endif
1521
}
1622
}

TestStack.BDDfy/Scanners/StepScanners/Fluent/IWhen.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System;
22
using System.Linq.Expressions;
33

4+
#if !NET35
5+
using System.Threading.Tasks;
6+
#endif
7+
48
namespace TestStack.BDDfy.Scanners.StepScanners.Fluent
59
{
610
public interface IWhen<TScenario> : IFluentScanner<TScenario>
@@ -15,6 +19,12 @@ public interface IWhen<TScenario> : IFluentScanner<TScenario>
1519
#else
1620
IAndWhen<TScenario> And(Expression<Action<TScenario>> andWhenStep, string stepTextTemplate = null);
1721
IThen<TScenario> Then(Expression<Action<TScenario>> thenStep, string stepTextTemplate = null);
22+
23+
IAndWhen<TScenario> And(Expression<Func<TScenario, Task>> andWhenStep, string stepTextTemplate = null);
24+
IThen<TScenario> Then(Expression<Func<TScenario, Task>> thenStep, string stepTextTemplate = null);
25+
26+
IAndWhen<TScenario> And(Expression<Func<TScenario, Task>> andWhenStep, bool includeInputsInStepTitle);
27+
IThen<TScenario> Then(Expression<Func<TScenario, Task>> thenStep, bool includeInputsInStepTitle);
1828
#endif
1929
}
2030
}

TestStack.BDDfy/Scanners/StepScanners/StepActionFactory.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
using System.Reflection;
33
using System.Security;
44
using System.Threading;
5-
using System.Threading.Tasks;
65
using TestStack.BDDfy.Processors;
76

7+
#if !NET35
8+
using System.Threading.Tasks;
9+
#endif
10+
811
namespace TestStack.BDDfy.Scanners.StepScanners
912
{
1013
public class StepActionFactory
@@ -24,6 +27,19 @@ public static Action<object> GetStepAction<TScenario>(Action<TScenario> action)
2427
});
2528
}
2629

30+
#if NET35
31+
private static void Run(Func<object> func)
32+
{
33+
func();
34+
}
35+
36+
#else
37+
public static Action<object> GetStepAction<TScenario>(Func<TScenario, Task> action)
38+
where TScenario : class
39+
{
40+
return o => Run(() => action((TScenario)o));
41+
}
42+
2743
private static void Run(Func<object> func)
2844
{
2945
var oldSyncContext = SynchronizationContext.Current;
@@ -67,5 +83,6 @@ static void SetSynchronizationContext(SynchronizationContext context)
6783
{
6884
SynchronizationContext.SetSynchronizationContext(context);
6985
}
86+
#endif
7087
}
7188
}

0 commit comments

Comments
 (0)