Skip to content

Commit 42c2e22

Browse files
committed
Merge pull request #122 from JakeGinnivan/ComplexTypes
Supporting complex types in examples, and expanded expression parser to ...
2 parents 452ec82 + 1b3b135 commit 42c2e22

13 files changed

Lines changed: 202 additions & 61 deletions

File tree

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

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,40 @@ protected string[] InheritedArrayInput2
2929

3030
public class ExpressionExtensionsTests : BaseClass
3131
{
32+
private class ContainerType
33+
{
34+
public int Target { get; set; }
35+
36+
public string Target2 { get; set; }
37+
38+
public ContainerType SubContainer { get; set; }
39+
40+
public override string ToString()
41+
{
42+
return Target2;
43+
}
44+
}
45+
3246
class ClassUnderTest
3347
{
3448
public void MethodWithoutArguments()
3549
{
36-
50+
3751
}
3852

3953
public void MethodWithInputs(int input1, string input2)
4054
{
41-
55+
4256
}
4357

4458
public void MethodWithArrayInputs(int[] input1, string[] input2)
4559
{
46-
60+
61+
}
62+
63+
public void MethodWithInputs(ContainerType subContainer)
64+
{
65+
4766
}
4867
}
4968

@@ -89,18 +108,22 @@ string GetInput2(string someInput)
89108
return someInput + " Input 2";
90109
}
91110

111+
ContainerType container = new ContainerType();
112+
92113
[Test]
93114
public void NoArguments()
94115
{
95116
var arguments = GetArguments(x => x.MethodWithoutArguments(), new ClassUnderTest());
96117
Assert.That(arguments.Length, Is.EqualTo(0));
97118
}
98119

99-
void AssertReturnedArguments(object[] arguments, object expectedArg1, object expectedArg2)
120+
void AssertReturnedArguments(object[] arguments, params object[] expectedArgs)
100121
{
101-
Assert.That(arguments.Length, Is.EqualTo(2));
102-
Assert.That(arguments[0], Is.EqualTo(expectedArg1));
103-
Assert.That(arguments[1], Is.EqualTo(expectedArg2));
122+
Assert.That(arguments.Length, Is.EqualTo(expectedArgs.Length));
123+
for (int i = 0; i < expectedArgs.Length; i++)
124+
{
125+
Assert.That(arguments[i], Is.EqualTo(expectedArgs[i]));
126+
}
104127
}
105128

106129
[Test]
@@ -132,14 +155,14 @@ public void InputArgumentsProvidedUsingProperty()
132155
var arguments = GetArguments(x => x.MethodWithInputs(Input1, Input2), new ClassUnderTest());
133156
AssertReturnedArguments(arguments, Input1, Input2);
134157
}
135-
158+
136159
[Test]
137160
public void InputArgumentsProvidedUsingInheritedFields()
138161
{
139162
var arguments = GetArguments(x => x.MethodWithInputs(InheritedInput1, InheritedInput2), new ClassUnderTest());
140163
AssertReturnedArguments(arguments, InheritedInput1, InheritedInput2);
141164
}
142-
165+
143166
[Test]
144167
public void InputArgumentsProvidedUsingMethodCallDoesNotThrow()
145168
{
@@ -150,14 +173,14 @@ public void InputArgumentsProvidedUsingMethodCallDoesNotThrow()
150173
public void ArrayInputsArgumentsProvidedInline()
151174
{
152175
var arguments = GetArguments(x => x.MethodWithArrayInputs(new[] { 1, 2 }, new[] { "3", "4" }), new ClassUnderTest());
153-
AssertReturnedArguments(arguments, new[] {1, 2}, new[] {"3", "4"});
176+
AssertReturnedArguments(arguments, new[] { 1, 2 }, new[] { "3", "4" });
154177
}
155178

156179
[Test]
157180
public void ArrayInputArgumentsProvidedUsingVariables()
158181
{
159-
var input1 = new[] {1, 2};
160-
var input2 = new[] {"3", "4"};
182+
var input1 = new[] { 1, 2 };
183+
var input2 = new[] { "3", "4" };
161184
var arguments = GetArguments(x => x.MethodWithArrayInputs(input1, input2), new ClassUnderTest());
162185
AssertReturnedArguments(arguments, input1, input2);
163186
}
@@ -176,6 +199,33 @@ public void ArrayInputArgumentsProvidedUsingProperty()
176199
AssertReturnedArguments(arguments, ArrayInput1, ArrayInput2);
177200
}
178201

202+
[Test]
203+
public void ComplexArgument()
204+
{
205+
container.Target = 1;
206+
container.SubContainer = new ContainerType { Target2 = "Foo" };
207+
208+
var arguments = GetArguments(x => x.MethodWithInputs(container.Target, container.SubContainer.Target2), new ClassUnderTest());
209+
AssertReturnedArguments(arguments, 1, "Foo");
210+
}
211+
212+
[Test]
213+
public void ComplexArgument2()
214+
{
215+
container.SubContainer = new ContainerType { Target2 = "Foo" };
216+
217+
var arguments = GetArguments(x => x.MethodWithInputs(container.SubContainer), new ClassUnderTest());
218+
AssertReturnedArguments(arguments, container.SubContainer);
219+
}
220+
221+
[Test]
222+
public void ComplexArgumentWhenContainerIsNull()
223+
{
224+
ContainerType nullContainer = null;
225+
var arguments = GetArguments(x => x.MethodWithInputs(nullContainer.SubContainer), new ClassUnderTest());
226+
AssertReturnedArguments(arguments, new object[] { null });
227+
}
228+
179229
[Test]
180230
public void ArrayInputArgumentsProvidedUsingInheritedProperty()
181231
{

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using ApprovalTests;
1+
using System.Runtime.CompilerServices;
2+
using ApprovalTests;
23
using NUnit.Framework;
34
using Shouldly;
45
using TestStack.BDDfy.Reporters;
@@ -31,6 +32,22 @@ public void FluentCanBeUsedWithExamples()
3132
Approvals.Verify(textReporter.ToString());
3233
}
3334

35+
[Test]
36+
[MethodImpl(MethodImplOptions.NoInlining)]
37+
public void ExampleTypeMismatch()
38+
{
39+
var ex = Should.Throw<UnassignableExampleException>(
40+
() => this.Given(() => WrongType.ShouldBe(1), "Given i use an example")
41+
.WithExamples(new ExampleTable("Wrong type")
42+
{
43+
new object(),
44+
new object[] { null }
45+
})
46+
.BDDfy());
47+
48+
ex.Message.ShouldBe("System.Object cannot be assigned to Int32 (Column: 'Wrong type', Row: 1)");
49+
}
50+
3451
private void AndIUseA(string multiWordHeading)
3552
{
3653
multiWordHeading.ShouldBeOneOf("", "val2");
@@ -44,7 +61,7 @@ private void GivenADifferentMethodWith(string prop2)
4461

4562
private void GivenADifferentMethodWithRandomArg(int foo)
4663
{
47-
64+
4865
}
4966

5067
private void ThenAllIsGood()
@@ -63,6 +80,7 @@ private void GivenMethodTaking__ExampleInt__(int exampleInt)
6380
exampleInt.ShouldBeInRange(1, 2);
6481
}
6582

83+
public int WrongType { get; set; }
6684
public int Prop1 { get; set; }
6785
private string _prop2 = null;
6886
private string multiWordHeading = null;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using NUnit.Framework;
3+
using Shouldly;
4+
5+
namespace TestStack.BDDfy.Tests.Scanner.StepScanners.Examples
6+
{
7+
public class ExampleValueTests
8+
{
9+
[Test]
10+
public void CanFormatAsStringTests()
11+
{
12+
new ExampleValue("Header", null, () => 0).GetValueAsString().ShouldBe("'null'");
13+
new ExampleValue("Header", 1, () => 0).GetValueAsString().ShouldBe("1");
14+
new ExampleValue("Header", new Object(), () => 0).GetValueAsString().ShouldBe("System.Object");
15+
new ExampleValue("Header", new[] {1, 2}, () => 0).GetValueAsString().ShouldBe("1, 2");
16+
}
17+
}
18+
}

TestStack.BDDfy.Tests/TestStack.BDDfy.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
<Compile Include="Scanner\FluentScanner\InlineAssertions.cs" />
9191
<Compile Include="Scanner\ReflectiveScanner\ReflectiveWithExamples.cs" />
9292
<Compile Include="Scanner\StepScanners\Examples\ExampleTableTests.cs" />
93+
<Compile Include="Scanner\StepScanners\Examples\ExampleValueTests.cs" />
9394
<Compile Include="Scanner\WhenStepScannerFactoryAsyncMethods.cs" />
9495
<Compile Include="Configuration\CustomProcessor.cs" />
9596
<Compile Include="Configuration\BatchProcessorsTests.cs" />

TestStack.BDDfy/Reporters/Html/ClassicReportBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ private void AddExampleRow(Scenario scenario, Result scenarioResult)
241241
{
242242
AddLine(string.Format("<td><Span class='{0}' style='margin-right:4px;' /></td>", scenario.Result));
243243
foreach (var exampleValue in scenario.Example.Values)
244-
AddLine(string.Format("<td>{0}</td>", HttpUtility.HtmlEncode(exampleValue.GetValue(typeof(string)))));
244+
AddLine(string.Format("<td>{0}</td>", HttpUtility.HtmlEncode(exampleValue.GetValueAsString())));
245245

246246
if (scenarioResult != Result.Failed)
247247
return;

TestStack.BDDfy/Reporters/Html/MetroReportBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ private void AddExampleRow(Scenario scenario, Result scenarioResult)
284284
{
285285
AddLine(string.Format("<td><Span class='{0}' style='margin-right:4px;' /></td>", scenario.Result));
286286
foreach (var exampleValue in scenario.Example.Values)
287-
AddLine(string.Format("<td>{0}</td>", HttpUtility.HtmlEncode(exampleValue.GetValue(typeof(string)))));
287+
AddLine(string.Format("<td>{0}</td>", HttpUtility.HtmlEncode(exampleValue.GetValueAsString())));
288288

289289
if (scenarioResult != Result.Failed)
290290
return;

TestStack.BDDfy/Reporters/TextReporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private void WriteExamples(Scenario exampleScenario, IEnumerable<Scenario> scena
9797
? null
9898
: string.Format("Step: {0} failed with exception: {1}", failingStep.Title, CreateExceptionMessage(failingStep));
9999

100-
addRow(scenario.Example.Values.Select(e => (string)e.GetValue(typeof(string))), scenario.Result.ToString(), error);
100+
addRow(scenario.Example.Values.Select(e => e.GetValueAsString()), scenario.Result.ToString(), error);
101101
}
102102

103103
foreach (var row in rows)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Linq;
3+
4+
namespace TestStack.BDDfy
5+
{
6+
internal static class ArgumentCleaningExtensions
7+
{
8+
internal static object[] FlattenArrays(this object[] inputs)
9+
{
10+
return inputs.Select(FlattenArray).ToArray();
11+
}
12+
13+
public static object FlattenArray(this object input)
14+
{
15+
var inputArray = input as Array;
16+
if (inputArray != null)
17+
{
18+
var temp = (from object arrElement in inputArray select GetSafeString(arrElement)).ToArray();
19+
return string.Join(", ", temp);
20+
}
21+
22+
if (input == null) return "'null'";
23+
24+
return input;
25+
}
26+
27+
static string GetSafeString(object input)
28+
{
29+
if (input == null)
30+
return "'null'";
31+
32+
return input.ToString();
33+
}
34+
}
35+
}

TestStack.BDDfy/Scanners/StepScanners/Examples/ExampleValue.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public object GetValue(Type targetType)
3434
var stringValue = _underlyingValue as string;
3535
if (_underlyingValue == null)
3636
{
37-
if (targetType.IsValueType && !(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof (Nullable<>)))
37+
if (targetType.IsValueType && !(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)))
3838
{
3939
var valueAsString = string.IsNullOrEmpty(stringValue) ? "<null>" : string.Format("\"{0}\"", _underlyingValue);
4040
throw new ArgumentException(string.Format("Cannot convert {0} to {1} (Column: '{2}', Row: {3})", valueAsString, targetType.Name, Header, Row));
@@ -49,10 +49,20 @@ public object GetValue(Type targetType)
4949
if (targetType.IsEnum && _underlyingValue is string)
5050
return Enum.Parse(targetType, (string)_underlyingValue);
5151

52-
if (targetType == typeof (DateTime))
52+
if (targetType == typeof(DateTime))
5353
return DateTime.Parse(stringValue);
5454

55-
return Convert.ChangeType(_underlyingValue, targetType);
55+
try
56+
{
57+
return Convert.ChangeType(_underlyingValue, targetType);
58+
}
59+
catch (InvalidCastException ex)
60+
{
61+
throw new UnassignableExampleException(string.Format(
62+
"{0} cannot be assigned to {1} (Column: '{2}', Row: {3})",
63+
_underlyingValue == null ? "<null>" : _underlyingValue.ToString(),
64+
targetType.Name, Header, Row), ex, this);
65+
}
5666
}
5767

5868
public bool ValueHasBeenUsed { get; private set; }
@@ -61,5 +71,10 @@ public override string ToString()
6171
{
6272
return string.Join("{0}: {1}", Header, _underlyingValue);
6373
}
74+
75+
public string GetValueAsString()
76+
{
77+
return _underlyingValue.FlattenArray().ToString();
78+
}
6479
}
6580
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
4+
namespace TestStack.BDDfy
5+
{
6+
[Serializable]
7+
public class UnassignableExampleException : Exception
8+
{
9+
public UnassignableExampleException(string message, Exception inner, ExampleValue exampleValue) : base(message, inner)
10+
{
11+
ExampleValue = exampleValue;
12+
}
13+
14+
protected UnassignableExampleException(
15+
SerializationInfo info,
16+
StreamingContext context) : base(info, context)
17+
{
18+
}
19+
20+
public ExampleValue ExampleValue { get; private set; }
21+
}
22+
}

0 commit comments

Comments
 (0)