Skip to content

Commit f8989c6

Browse files
committed
Merge pull request #196 from JakeGinnivan/ExpressionParsingIssue
Expression parsing issue
2 parents 2f2a3fd + dae616b commit f8989c6

File tree

3 files changed

+96
-61
lines changed

3 files changed

+96
-61
lines changed

TestStack.BDDfy.Tests/Scanner/FluentScanner/ExpressionExtensionsTests.cs

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Linq.Expressions;
45
using NUnit.Framework;
@@ -67,6 +68,11 @@ public void MethodWithInputs(ContainerType subContainer)
6768

6869
}
6970

71+
public void MethodWithNullableArg(decimal? nullableInput)
72+
{
73+
74+
}
75+
7076
public Bar Foo { get; set; }
7177

7278
public class Bar
@@ -77,9 +83,14 @@ public void Baz()
7783
}
7884
}
7985

80-
object[] GetArguments(Expression<Action<ClassUnderTest>> action, ClassUnderTest instance)
86+
List<object> GetArgumentValues(Expression<Action<ClassUnderTest>> action, ClassUnderTest instance)
87+
{
88+
return action.ExtractArguments(instance).Select(o => o.Value).ToList();
89+
}
90+
91+
List<StepArgument> GetArguments(Expression<Action<ClassUnderTest>> action, ClassUnderTest instance)
8192
{
82-
return action.ExtractArguments(instance).Select(o => o.Value).ToArray();
93+
return action.ExtractArguments(instance).ToList();
8394
}
8495

8596
int _input1 = 1;
@@ -124,13 +135,13 @@ string GetInput2(string someInput)
124135
[Fact]
125136
public void NoArguments()
126137
{
127-
var arguments = GetArguments(x => x.MethodWithoutArguments(), new ClassUnderTest());
128-
arguments.Length.ShouldBe(0);
138+
var arguments = GetArgumentValues(x => x.MethodWithoutArguments(), new ClassUnderTest());
139+
arguments.Count.ShouldBe(0);
129140
}
130141

131-
void AssertReturnedArguments(object[] arguments, params object[] expectedArgs)
142+
void AssertReturnedArguments(List<object> arguments, params object[] expectedArgs)
132143
{
133-
arguments.Length.ShouldBe(expectedArgs.Length);
144+
arguments.Count.ShouldBe(expectedArgs.Length);
134145
for (int i = 0; i < expectedArgs.Length; i++)
135146
{
136147
arguments[i].ShouldBe(expectedArgs[i]);
@@ -140,7 +151,7 @@ void AssertReturnedArguments(object[] arguments, params object[] expectedArgs)
140151
[Fact]
141152
public void InputArgumentsPassedInline()
142153
{
143-
var arguments = GetArguments(x => x.MethodWithInputs(1, "2"), new ClassUnderTest());
154+
var arguments = GetArgumentValues(x => x.MethodWithInputs(1, "2"), new ClassUnderTest());
144155
AssertReturnedArguments(arguments, 1, "2");
145156
}
146157

@@ -149,41 +160,60 @@ public void InputArgumentsProvidedUsingVariables()
149160
{
150161
int input1 = 1;
151162
const string input2 = "2";
152-
var arguments = GetArguments(x => x.MethodWithInputs(input1, input2), new ClassUnderTest());
163+
var arguments = GetArgumentValues(x => x.MethodWithInputs(input1, input2), new ClassUnderTest());
153164
AssertReturnedArguments(arguments, input1, input2);
154165
}
155166

156167
[Fact]
157168
public void InputArgumentsProvidedUsingFields()
158169
{
159-
var arguments = GetArguments(x => x.MethodWithInputs(_input1, ConstInput2), new ClassUnderTest());
170+
var arguments = GetArgumentValues(x => x.MethodWithInputs(_input1, ConstInput2), new ClassUnderTest());
160171
AssertReturnedArguments(arguments, _input1, ConstInput2);
161172
}
162173

174+
[Fact]
175+
public void InputArgumentsProvidedWhenCastIsInvolved()
176+
{
177+
// For some reason default(decimal) will cause a different expression when passing to a nullable method than
178+
// if we have input1 = 1m; No idea why...
179+
var input1 = default(decimal);
180+
var arguments = GetArguments(x => x.MethodWithNullableArg(input1), new ClassUnderTest());
181+
input1 = 1;
182+
AssertReturnedArguments(arguments.Select(a => a.Value).ToList(), input1);
183+
}
184+
185+
[Fact]
186+
public void InputArgWithImplicitCast()
187+
{
188+
int input1 = 1;
189+
var arguments = GetArgumentValues(x => x.MethodWithNullableArg(input1), new ClassUnderTest());
190+
AssertReturnedArguments(arguments, input1);
191+
}
192+
163193
[Fact]
164194
public void InputArgumentsProvidedUsingProperty()
165195
{
166-
var arguments = GetArguments(x => x.MethodWithInputs(Input1, Input2), new ClassUnderTest());
196+
var arguments = GetArgumentValues(x => x.MethodWithInputs(Input1, Input2), new ClassUnderTest());
167197
AssertReturnedArguments(arguments, Input1, Input2);
168198
}
169199

170200
[Fact]
171201
public void InputArgumentsProvidedUsingInheritedFields()
172202
{
173-
var arguments = GetArguments(x => x.MethodWithInputs(InheritedInput1, InheritedInput2), new ClassUnderTest());
203+
var arguments = GetArgumentValues(x => x.MethodWithInputs(InheritedInput1, InheritedInput2), new ClassUnderTest());
174204
AssertReturnedArguments(arguments, InheritedInput1, InheritedInput2);
175205
}
176206

177207
[Fact]
178208
public void InputArgumentsProvidedUsingMethodCallDoesNotThrow()
179209
{
180-
Should.NotThrow(() => GetArguments(x => x.MethodWithInputs(GetInput1(10), GetInput2("Test")), new ClassUnderTest()));
210+
Should.NotThrow(() => GetArgumentValues(x => x.MethodWithInputs(GetInput1(10), GetInput2("Test")), new ClassUnderTest()));
181211
}
182212

183213
[Fact]
184214
public void ArrayInputsArgumentsProvidedInline()
185215
{
186-
var arguments = GetArguments(x => x.MethodWithArrayInputs(new[] { 1, 2 }, new[] { "3", "4" }), new ClassUnderTest());
216+
var arguments = GetArgumentValues(x => x.MethodWithArrayInputs(new[] { 1, 2 }, new[] { "3", "4" }), new ClassUnderTest());
187217
AssertReturnedArguments(arguments, new[] { 1, 2 }, new[] { "3", "4" });
188218
}
189219

@@ -192,21 +222,21 @@ public void ArrayInputArgumentsProvidedUsingVariables()
192222
{
193223
var input1 = new[] { 1, 2 };
194224
var input2 = new[] { "3", "4" };
195-
var arguments = GetArguments(x => x.MethodWithArrayInputs(input1, input2), new ClassUnderTest());
225+
var arguments = GetArgumentValues(x => x.MethodWithArrayInputs(input1, input2), new ClassUnderTest());
196226
AssertReturnedArguments(arguments, input1, input2);
197227
}
198228

199229
[Fact]
200230
public void ArrayInputArgumentsProvidedUsingFields()
201231
{
202-
var arguments = GetArguments(x => x.MethodWithArrayInputs(_arrayInput1, _arrayInput2), new ClassUnderTest());
232+
var arguments = GetArgumentValues(x => x.MethodWithArrayInputs(_arrayInput1, _arrayInput2), new ClassUnderTest());
203233
AssertReturnedArguments(arguments, _arrayInput1, _arrayInput2);
204234
}
205235

206236
[Fact]
207237
public void ArrayInputArgumentsProvidedUsingProperty()
208238
{
209-
var arguments = GetArguments(x => x.MethodWithArrayInputs(ArrayInput1, ArrayInput2), new ClassUnderTest());
239+
var arguments = GetArgumentValues(x => x.MethodWithArrayInputs(ArrayInput1, ArrayInput2), new ClassUnderTest());
210240
AssertReturnedArguments(arguments, ArrayInput1, ArrayInput2);
211241
}
212242

@@ -216,7 +246,7 @@ public void ComplexArgument()
216246
container.Target = 1;
217247
container.SubContainer = new ContainerType { Target2 = "Foo" };
218248

219-
var arguments = GetArguments(x => x.MethodWithInputs(container.Target, container.SubContainer.Target2), new ClassUnderTest());
249+
var arguments = GetArgumentValues(x => x.MethodWithInputs(container.Target, container.SubContainer.Target2), new ClassUnderTest());
220250
AssertReturnedArguments(arguments, 1, "Foo");
221251
}
222252

@@ -226,7 +256,7 @@ public void ComplexArgumentMethodCall()
226256
container.Target = 1;
227257
container.SubContainer = new ContainerType { Target2 = "Foo" };
228258

229-
var arguments = GetArguments(x => x.MethodWithInputs(container.Target, container.SubContainer.ToString()), new ClassUnderTest());
259+
var arguments = GetArgumentValues(x => x.MethodWithInputs(container.Target, container.SubContainer.ToString()), new ClassUnderTest());
230260
AssertReturnedArguments(arguments, 1, "Foo");
231261
}
232262

@@ -235,29 +265,29 @@ public void ComplexArgument2()
235265
{
236266
container.SubContainer = new ContainerType { Target2 = "Foo" };
237267

238-
var arguments = GetArguments(x => x.MethodWithInputs(container.SubContainer), new ClassUnderTest());
268+
var arguments = GetArgumentValues(x => x.MethodWithInputs(container.SubContainer), new ClassUnderTest());
239269
AssertReturnedArguments(arguments, container.SubContainer);
240270
}
241271

242272
[Fact]
243273
public void ComplexArgumentWhenContainerIsNull()
244274
{
245275
ContainerType nullContainer = null;
246-
var arguments = GetArguments(x => x.MethodWithInputs(nullContainer.SubContainer), new ClassUnderTest());
276+
var arguments = GetArgumentValues(x => x.MethodWithInputs(nullContainer.SubContainer), new ClassUnderTest());
247277
AssertReturnedArguments(arguments, new object[] { null });
248278
}
249279

250280
[Fact]
251281
public void MethodCallValue()
252282
{
253-
var arguments = GetArguments(x => x.MethodWithInputs(GetNumberThree(), GetFooString()), new ClassUnderTest());
283+
var arguments = GetArgumentValues(x => x.MethodWithInputs(GetNumberThree(), GetFooString()), new ClassUnderTest());
254284
AssertReturnedArguments(arguments, new object[] { 3, "Foo" });
255285
}
256286

257287
[Fact]
258288
public void DeepPropertyCall()
259289
{
260-
var arguments = GetArguments(x => x.Foo.Baz(), new ClassUnderTest());
290+
var arguments = GetArgumentValues(x => x.Foo.Baz(), new ClassUnderTest());
261291
arguments.ShouldBeEmpty();
262292
}
263293

@@ -274,14 +304,14 @@ private int GetNumberThree()
274304
[Fact]
275305
public void ArrayInputArgumentsProvidedUsingInheritedProperty()
276306
{
277-
var arguments = GetArguments(x => x.MethodWithArrayInputs(InheritedArrayInput1, InheritedArrayInput2), new ClassUnderTest());
307+
var arguments = GetArgumentValues(x => x.MethodWithArrayInputs(InheritedArrayInput1, InheritedArrayInput2), new ClassUnderTest());
278308
AssertReturnedArguments(arguments, InheritedArrayInput1, InheritedArrayInput2);
279309
}
280310

281311
[Fact]
282312
public void StaticField()
283313
{
284-
Should.NotThrow(() => GetArguments(x => x.MethodWithInputs(GetInput1(10), GetInput2(string.Empty)), new ClassUnderTest()));
314+
Should.NotThrow(() => GetArgumentValues(x => x.MethodWithInputs(GetInput1(10), GetInput2(string.Empty)), new ClassUnderTest()));
285315
}
286316
}
287317
}

TestStack.BDDfy/Processors/TestRunner.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ public void Process(Story story)
1616
var executor = new ScenarioExecutor(scenario);
1717
executor.InitializeScenario();
1818

19+
if (scenario.Example != null)
20+
{
21+
var unusedValue = scenario.Example.Values.FirstOrDefault(v => !v.ValueHasBeenUsed);
22+
if (unusedValue != null) throw new UnusedExampleException(unusedValue);
23+
}
24+
1925
var stepFailed = false;
2026
foreach (var executionStep in scenario.Steps)
2127
{
@@ -30,12 +36,6 @@ public void Process(Story story)
3036

3137
stepFailed = true;
3238
}
33-
34-
if (scenario.Example != null && scenario.Result == Result.Passed)
35-
{
36-
var unusedValue = scenario.Example.Values.FirstOrDefault(v => !v.ValueHasBeenUsed);
37-
if (unusedValue != null) throw new UnusedExampleException(unusedValue);
38-
}
3939
}
4040
}
4141

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

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,45 @@ public IEnumerable<StepArgument> ExtractArguments(LambdaExpression methodCallExp
3232

3333
protected override Expression VisitMethodCall(MethodCallExpression node)
3434
{
35-
var arguments = node.Arguments.Select(a =>
35+
var arguments = node.Arguments.Select(ExtractStepArgument);
36+
_arguments.AddRange(arguments);
37+
return node;
38+
}
39+
40+
private static StepArgument ExtractStepArgument(Expression a)
41+
{
42+
switch (a.NodeType)
3643
{
37-
switch (a.NodeType)
38-
{
39-
case ExpressionType.MemberAccess:
40-
var memberExpression = (MemberExpression)a;
41-
var field = memberExpression.Member as FieldInfo;
42-
string name;
43-
Type parameterType;
44-
bool isReadOnly;
45-
if (field != null)
46-
{
47-
name = field.Name;
48-
parameterType = field.FieldType;
49-
isReadOnly = field.IsInitOnly;
50-
}
51-
else
52-
{
53-
var propertyInfo = (PropertyInfo)memberExpression.Member;
54-
name = propertyInfo.Name;
55-
parameterType = propertyInfo.PropertyType;
56-
isReadOnly = !propertyInfo.CanWrite;
57-
}
44+
case ExpressionType.MemberAccess:
45+
var memberExpression = (MemberExpression) a;
46+
var field = memberExpression.Member as FieldInfo;
47+
string name;
48+
Type parameterType;
49+
bool isReadOnly;
50+
if (field != null)
51+
{
52+
name = field.Name;
53+
parameterType = field.FieldType;
54+
isReadOnly = field.IsInitOnly;
55+
}
56+
else
57+
{
58+
var propertyInfo = (PropertyInfo) memberExpression.Member;
59+
name = propertyInfo.Name;
60+
parameterType = propertyInfo.PropertyType;
61+
isReadOnly = !propertyInfo.CanWrite;
62+
}
5863

59-
var getValue = GetValue(memberExpression);
60-
var setValue = isReadOnly ? null : SetValue(memberExpression, parameterType);
64+
var getValue = GetValue(memberExpression);
65+
var setValue = isReadOnly ? null : SetValue(memberExpression, parameterType);
6166

62-
return new StepArgument(name, parameterType, getValue, setValue);
63-
default:
64-
return new StepArgument(GetValue(a));
65-
}
66-
});
67-
_arguments.AddRange(arguments);
68-
return node;
67+
return new StepArgument(name, parameterType, getValue, setValue);
68+
69+
case ExpressionType.Convert:
70+
return ExtractStepArgument(((UnaryExpression)a).Operand);
71+
default:
72+
return new StepArgument(GetValue(a));
73+
}
6974
}
7075

7176
private static Func<object> GetValue(Expression a)

0 commit comments

Comments
 (0)