Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="shouldly" Version="4.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

### Step name will be built from step title
i have a red car with a crazy horn
When i honk the horn at volume high
Then i verify diagnostics a red car honked crazy horn at high volume

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

### Step names are built from all chaned methods
Given i have a car with color red with horn crazy
When i honk the horn
Then i verify diagnostics a red car honked crazy horn

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Shouldly;
using TestStack.BDDfy.Reporters;
using TestStack.BDDfy.Reporters.MarkDown;
using TestStack.BDDfy.Tests.Reporters;
using Xunit;

namespace TestStack.BDDfy.Tests.Scanner.FluentScanner
{
public class StepsWithChainedMethods
{
private SutStepBuilder _sutStepBuilder = new();
private class SutStepBuilder
{
public string state;
private string color;
private string horn;

public SutStepBuilder with_color(string color) { this.color = color; return this; }
public SutStepBuilder with_horn(string horn) { this.horn = horn; return this; }
internal void at_volume(string volume) { state+= $" at {volume} volume"; }
internal SutStepBuilder have_a_car() { state = "a"; return this; }
internal SutStepBuilder honk_the_horn() { state+= $" {color} car honked {horn} horn"; return this; }

internal void verify_diagnostics(string v) => state.ShouldBe(v);
}

[Fact]
public void StepNamesAreBuiltFromAllChanedMethods()
{
var story = this.Given(_ => i().have_a_car().with_color("red").with_horn("crazy"))
.When(_ => i().honk_the_horn())
.Then(_ => i().verify_diagnostics("a red car honked crazy horn"))
.LazyBDDfy();

var result = story.Run();
var model = new[] {result}.ToReportModel();
var fileReport = new FileReportModel(model);
ReportApprover.Approve(fileReport, new MarkDownReportBuilder());
}

[Fact]
public void StepNameWillBeBuiltFromStepTitle()
{
var story = this.Given(_ => i().have_a_car().with_color("red").with_horn("crazy"), "i have a {0} car with a {1} horn")
.When(_ => i().honk_the_horn().at_volume("high"))
.Then(_ => i().verify_diagnostics("a red car honked crazy horn at high volume"))
.LazyBDDfy();

var result = story.Run();
var model = new[] { result }.ToReportModel();
var fileReport = new FileReportModel(model);
ReportApprover.Approve(fileReport, new MarkDownReportBuilder());
}

private SutStepBuilder i() => _sutStepBuilder;
}
}
16 changes: 16 additions & 0 deletions src/TestStack.BDDfy.Tests/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Runtime.CompilerServices;
using TestStack.BDDfy.Configuration;
using TestStack.BDDfy.Tests;

namespace TestStack.BDDfy.Samples
{
public class Startup
{
[ModuleInitializer]
public static void Initialize()
{
Configurator.Processors.Add(() => new XUnitOutputReporter());
Configurator.Processors.ConsoleReport.Enable();
}
}
}
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy.Tests/TestStack.BDDfy.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="nsubstitute" Version="5.3.0" />
<PackageReference Include="shouldly" Version="4.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
Expand Down
4 changes: 2 additions & 2 deletions src/TestStack.BDDfy/Abstractions/DefaultStepTitleFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal class DefaultStepTitleFactory : IStepTitleFactory
public bool IncludeInputsInStepTitle { get; set; } = true;

public StepTitle Create(
string stepTextTemplate,
string? stepTextTemplate,
bool? includeInputsInStepTitle,
MethodInfo methodInfo,
StepArgument[] inputArguments,
Expand All @@ -19,7 +19,7 @@ public StepTitle Create(
{
string createTitle()
{
var flatInputArray = inputArguments.Select(o => o.Value).FlattenArrays();
var flatInputArray = inputArguments.Select(o => o.Value!).FlattenArrays();
var name = methodInfo.Name;
var titleAttribute = methodInfo.GetCustomAttribute<StepTitleAttribute>(true);
var executableAttribute = methodInfo.GetCustomAttribute<ExecutableAttribute>(true);
Expand Down
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy/Abstractions/IStepTitleFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public interface IStepTitleFactory
bool IncludeInputsInStepTitle { get; set; }

public StepTitle Create(
string stepTextTemplate,
string? stepTextTemplate,
bool? includeInputsInStepTitle,
MethodInfo methodInfo,
StepArgument[] inputArguments,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public static IEnumerable<StepArgument> ExtractArguments<T>(this Expression<Func

private class ArgumentExtractorVisitor : ExpressionVisitor
{
private List<StepArgument> _arguments;
private object _value;
private List<StepArgument> _arguments = [];
private object? _value;

public IEnumerable<StepArgument> ExtractArguments(LambdaExpression methodCallExpression, object value)
public IEnumerable<StepArgument> ExtractArguments(LambdaExpression methodCallExpression, object? value)
{
_arguments = [];
_value = value;
Expand All @@ -34,6 +34,8 @@ public IEnumerable<StepArgument> ExtractArguments(LambdaExpression methodCallExp

protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Object is MethodCallExpression methodCallExpression) Visit(methodCallExpression);

var arguments = node.Arguments.Select(ExtractStepArgument);
_arguments.AddRange(arguments);
return node;
Expand Down Expand Up @@ -75,32 +77,32 @@ private StepArgument ExtractStepArgument(Expression a)
}
}

private Func<object> GetValue(Expression a)
private Func<object> GetValue(Expression expression)
{
// If the expression is a member access on the lambda parameter (e.g. _ => _.Prop)
// replace the parameter with the supplied _value so the compiled delegate can be invoked
if (a is MemberExpression memberExpression && memberExpression.Expression is ParameterExpression)
if (expression is MemberExpression memberExpression && memberExpression.Expression is ParameterExpression)
{
var replaced = Expression.Convert(Expression.MakeMemberAccess(Expression.Constant(_value), memberExpression.Member), typeof(object));
return Expression.Lambda<Func<object>>(replaced).Compile();
}

return Expression.Lambda<Func<object>>(Expression.Convert(a, typeof(object))).Compile();
return Expression.Lambda<Func<object>>(Expression.Convert(expression, typeof(object))).Compile();
}

private Action<object> SetValue(Expression a, Type parameterType)
private Action<object> SetValue(Expression expression, Type parameterType)
{
var parameter = Expression.Parameter(typeof(object));
var unaryExpression = Expression.Convert(parameter, parameterType);

if (a is MemberExpression memberExpression && memberExpression.Expression is ParameterExpression)
if (expression is MemberExpression memberExpression && memberExpression.Expression is ParameterExpression)
{
var memberAccess = Expression.MakeMemberAccess(Expression.Constant(_value), memberExpression.Member);
var assign = Expression.Assign(memberAccess, unaryExpression);
return Expression.Lambda<Action<object>>(assign, parameter).Compile();
}

var assignDefault = Expression.Assign(a, unaryExpression);
var assignDefault = Expression.Assign(expression, unaryExpression);
return Expression.Lambda<Action<object>>(assignDefault, parameter).Compile();
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ public FluentStepBuilder(TScenario testObject)

public IFluentStepBuilder<TScenario> Given(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.SetupState, false, "Given");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.SetupState, false, "Given");
return this;
}

Expand All @@ -422,7 +422,7 @@ public IFluentStepBuilder<TScenario> Given(Expression<Action<TScenario>> step)

public IFluentStepBuilder<TScenario> Given(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.SetupState, false, "Given");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.SetupState, false, "Given");
return this;
}

Expand Down Expand Up @@ -463,7 +463,7 @@ public IFluentStepBuilder<TScenario> Given(string title)
}
public IFluentStepBuilder<TScenario> When(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.Transition, false, "When");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.Transition, false, "When");
return this;
}

Expand All @@ -481,7 +481,7 @@ public IFluentStepBuilder<TScenario> When(Expression<Action<TScenario>> step)

public IFluentStepBuilder<TScenario> When(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.Transition, false, "When");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.Transition, false, "When");
return this;
}

Expand Down Expand Up @@ -522,7 +522,7 @@ public IFluentStepBuilder<TScenario> When(string title)
}
public IFluentStepBuilder<TScenario> Then(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.Assertion, true, "Then");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.Assertion, true, "Then");
return this;
}

Expand All @@ -540,7 +540,7 @@ public IFluentStepBuilder<TScenario> Then(Expression<Action<TScenario>> step)

public IFluentStepBuilder<TScenario> Then(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.Assertion, true, "Then");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.Assertion, true, "Then");
return this;
}

Expand Down Expand Up @@ -581,7 +581,7 @@ public IFluentStepBuilder<TScenario> Then(string title)
}
public IFluentStepBuilder<TScenario> And(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.ConsecutiveStep, false, "And");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.ConsecutiveStep, false, "And");
return this;
}

Expand All @@ -599,7 +599,7 @@ public IFluentStepBuilder<TScenario> And(Expression<Action<TScenario>> step)

public IFluentStepBuilder<TScenario> And(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.ConsecutiveStep, false, "And");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.ConsecutiveStep, false, "And");
return this;
}

Expand Down Expand Up @@ -640,13 +640,13 @@ public IFluentStepBuilder<TScenario> And(string title)
}
public IFluentStepBuilder<TScenario> But(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.ConsecutiveStep, false, "But");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.ConsecutiveStep, false, "But");
return this;
}

public IFluentStepBuilder<TScenario> But(Expression<Action<TScenario>> step, bool includeInputsInStepTitle)
{
scanner.AddStep(step, null, includeInputsInStepTitle, true, ExecutionOrder.ConsecutiveStep, false, "But");
scanner.AddStep(step, null, includeInputsInStepTitle, false, ExecutionOrder.ConsecutiveStep, false, "But");
return this;
}

Expand All @@ -658,7 +658,7 @@ public IFluentStepBuilder<TScenario> But(Expression<Action<TScenario>> step)

public IFluentStepBuilder<TScenario> But(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, true, ExecutionOrder.ConsecutiveStep, false, "But");
scanner.AddStep(step, stepTextTemplate, false, true, ExecutionOrder.ConsecutiveStep, false, "But");
return this;
}

Expand Down Expand Up @@ -699,7 +699,7 @@ public IFluentStepBuilder<TScenario> But(string title)
}
public IFluentStepBuilder<TScenario> TearDownWith(Expression<Action<TScenario>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, false, ExecutionOrder.TearDown, false, "");
scanner.AddStep(step, stepTextTemplate, false, false, ExecutionOrder.TearDown, false, "");
return this;
}

Expand All @@ -717,7 +717,7 @@ public IFluentStepBuilder<TScenario> TearDownWith(Expression<Action<TScenario>>

public IFluentStepBuilder<TScenario> TearDownWith(Expression<Func<TScenario, Task>> step, string stepTextTemplate)
{
scanner.AddStep(step, stepTextTemplate, null, false, ExecutionOrder.TearDown, false, "");
scanner.AddStep(step, stepTextTemplate, false, false, ExecutionOrder.TearDown, false, "");
return this;
}

Expand Down
Loading
Loading