From be3d50201924e356ed014b1e02f66c591800b9ca Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Tue, 26 May 2026 11:59:39 +0100 Subject: [PATCH 1/6] avoid calling _createTitle function multiple times. --- src/TestStack.BDDfy/StepTitle.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/TestStack.BDDfy/StepTitle.cs b/src/TestStack.BDDfy/StepTitle.cs index d3e8daec..79721b2a 100644 --- a/src/TestStack.BDDfy/StepTitle.cs +++ b/src/TestStack.BDDfy/StepTitle.cs @@ -5,11 +5,9 @@ namespace TestStack.BDDfy public class StepTitle { private readonly Func _createTitle; + private string _title; - public StepTitle(string title) - { - _createTitle = () => title; - } + public StepTitle(string title) => _createTitle = () => _title = title; public StepTitle(Func createTitle) { @@ -23,7 +21,7 @@ public static implicit operator string(StepTitle title) public override string ToString() { - return _createTitle(); + return _title ??= _createTitle(); } } } \ No newline at end of file From 97c09bf37ef4e4066a8b44054568de0534d754d9 Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Tue, 26 May 2026 12:11:01 +0100 Subject: [PATCH 2/6] cache step title text once generated --- src/TestStack.BDDfy/Step.cs | 12 ++++++------ src/TestStack.BDDfy/StepTitle.cs | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/TestStack.BDDfy/Step.cs b/src/TestStack.BDDfy/Step.cs index ac331ede..1f667a7f 100644 --- a/src/TestStack.BDDfy/Step.cs +++ b/src/TestStack.BDDfy/Step.cs @@ -6,11 +6,11 @@ namespace TestStack.BDDfy { public class Step { - private readonly StepTitle _title; - + private readonly StepTitle _stepTitle; + private string _title; public Step( Func action, - StepTitle title, + StepTitle stepTitle, bool asserts, ExecutionOrder executionOrder, bool shouldReport, @@ -23,13 +23,13 @@ public Step( Result = Result.NotExecuted; Action = action; Arguments = arguments; - _title = title; + _stepTitle = stepTitle; } public Step(Step step) { Id = step.Id; - _title = step._title; + _stepTitle = step._stepTitle; Asserts = step.Asserts; ExecutionOrder = step.ExecutionOrder; ShouldReport = step.ShouldReport; @@ -46,7 +46,7 @@ public string Title { get { - return _title.ToString(); + return _title??= _stepTitle.ToString(); } } diff --git a/src/TestStack.BDDfy/StepTitle.cs b/src/TestStack.BDDfy/StepTitle.cs index 79721b2a..64250e57 100644 --- a/src/TestStack.BDDfy/StepTitle.cs +++ b/src/TestStack.BDDfy/StepTitle.cs @@ -5,9 +5,8 @@ namespace TestStack.BDDfy public class StepTitle { private readonly Func _createTitle; - private string _title; - public StepTitle(string title) => _createTitle = () => _title = title; + public StepTitle(string title) => _createTitle = () => title; public StepTitle(Func createTitle) { @@ -21,7 +20,7 @@ public static implicit operator string(StepTitle title) public override string ToString() { - return _title ??= _createTitle(); + return _createTitle(); } } } \ No newline at end of file From 8f6ff5e6878ea9d6b2bd9e5c64eb307312f16fd6 Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Tue, 26 May 2026 12:16:48 +0100 Subject: [PATCH 3/6] simplify property definition --- src/TestStack.BDDfy/Step.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/TestStack.BDDfy/Step.cs b/src/TestStack.BDDfy/Step.cs index 1f667a7f..b2111df2 100644 --- a/src/TestStack.BDDfy/Step.cs +++ b/src/TestStack.BDDfy/Step.cs @@ -42,16 +42,8 @@ public Step(Step step) internal Func Action { get; set; } public bool Asserts { get; private set; } public bool ShouldReport { get; private set; } - public string Title - { - get - { - return _title??= _stepTitle.ToString(); - } - } - + public string Title => _title??= _stepTitle.ToString(); public ExecutionOrder ExecutionOrder { get; private set; } - public Result Result { get; set; } public Exception Exception { get; set; } public int ExecutionSubOrder { get; set; } From 896582d16319e0fce1cf03665f1c91f568cff952 Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Tue, 26 May 2026 12:18:16 +0100 Subject: [PATCH 4/6] use implicit string conversion --- src/TestStack.BDDfy/Step.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TestStack.BDDfy/Step.cs b/src/TestStack.BDDfy/Step.cs index b2111df2..444af43c 100644 --- a/src/TestStack.BDDfy/Step.cs +++ b/src/TestStack.BDDfy/Step.cs @@ -42,7 +42,7 @@ public Step(Step step) internal Func Action { get; set; } public bool Asserts { get; private set; } public bool ShouldReport { get; private set; } - public string Title => _title??= _stepTitle.ToString(); + public string Title => _title??= _stepTitle; public ExecutionOrder ExecutionOrder { get; private set; } public Result Result { get; set; } public Exception Exception { get; set; } From cabbb803375c29c23078f3ec616d3cd3b4221a84 Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Tue, 26 May 2026 23:36:07 +0100 Subject: [PATCH 5/6] add pure attribute to GWT api methods to help compile time suggestions when Bddfy() is not called --- .github/copilot-instructions.md | 4 + src/TestStack.BDDfy/Properties/Annotations.cs | 14 - .../Scanners/StepScanners/Fluent/FluentApi.cs | 303 +++++++++++------- .../Scanners/StepScanners/Fluent/FluentApi.tt | 210 ++++++------ 4 files changed, 309 insertions(+), 222 deletions(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..233b8675 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,4 @@ +# Copilot Instructions + +## Project Guidelines +- Repository preference: format FluentStepScannerExtensions extension methods with multiline parameters and expression-bodied single-line where clauses; add [Pure] attribute to IFluentStepBuilder members. \ No newline at end of file diff --git a/src/TestStack.BDDfy/Properties/Annotations.cs b/src/TestStack.BDDfy/Properties/Annotations.cs index 27f81204..95b14c20 100644 --- a/src/TestStack.BDDfy/Properties/Annotations.cs +++ b/src/TestStack.BDDfy/Properties/Annotations.cs @@ -354,20 +354,6 @@ public PublicAPIAttribute([NotNull] string comment) [ExcludeFromCodeCoverage] public sealed class InstantHandleAttribute : Attribute { } - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute - /// - /// - /// [Pure] private int Multiply(int x, int y) { return x * y; } - /// public void Foo() { - /// const int a = 2, b = 2; - /// Multiply(a, b); // Waring: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method, Inherited = true)] - public sealed class PureAttribute : Attribute { } - /// /// Indicates that a parameter is a path to a file or a folder /// within a web project. Path can be relative or absolute, diff --git a/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.cs b/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.cs index 1d67a9c2..c5e3f0fb 100644 --- a/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.cs +++ b/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Linq.Expressions; using System.Threading.Tasks; using TestStack.BDDfy.Configuration; @@ -10,139 +11,160 @@ namespace TestStack.BDDfy { public static class FluentStepScannerExtensions { - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, string stepTextTemplate) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, stepTextTemplate); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, string stepTextTemplate) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, stepTextTemplate); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step) - where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Action step, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).Given(step, title); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Func step, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).Given(step, title); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> action) - where TScenario : class - { - return new FluentStepBuilder(testObject).Given(action); - } - - public static IFluentStepBuilder Given(this TScenario testObject, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).Given(title); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, string stepTextTemplate) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, stepTextTemplate); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, string stepTextTemplate) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, stepTextTemplate); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step) - where TScenario: class - { - return new FluentStepBuilder(testObject).When(step); - } - - public static IFluentStepBuilder When(this TScenario testObject, Action step, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).When(step, title); - } - - public static IFluentStepBuilder When(this TScenario testObject, Func step, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).When(step, title); - } - - public static IFluentStepBuilder When(this TScenario testObject, string title) - where TScenario : class - { - return new FluentStepBuilder(testObject).When(title); - } + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + string stepTextTemplate) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step, stepTextTemplate); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + string stepTextTemplate) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step, stepTextTemplate); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step) + where TScenario: class + => new FluentStepBuilder(testObject).Given(step); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Action step, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).Given(step, title); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Func step, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).Given(step, title); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> action) + where TScenario : class + => new FluentStepBuilder(testObject).Given(action); + + public static IFluentStepBuilder Given( + this TScenario testObject, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).Given(title); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + string stepTextTemplate) + where TScenario: class + => new FluentStepBuilder(testObject).When(step, stepTextTemplate); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) + where TScenario: class + => new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step) + where TScenario: class + => new FluentStepBuilder(testObject).When(step); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + string stepTextTemplate) + where TScenario: class + => new FluentStepBuilder(testObject).When(step, stepTextTemplate); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) + where TScenario: class + => new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step) + where TScenario: class + => new FluentStepBuilder(testObject).When(step); + + public static IFluentStepBuilder When( + this TScenario testObject, + Action step, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).When(step, title); + + public static IFluentStepBuilder When( + this TScenario testObject, + Func step, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).When(step, title); + + public static IFluentStepBuilder When( + this TScenario testObject, + string title) + where TScenario : class + => new FluentStepBuilder(testObject).When(title); } public interface IFluentStepBuilder where TScenario: class { + [Pure] TScenario TestObject { get; } + [Pure] IFluentStepBuilder Given(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder Given(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder Given(Expression> step); + [Pure] IFluentStepBuilder Given(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder Given(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder Given(Expression> step); + [Pure] IFluentStepBuilder Given(Action step, string title); + [Pure] IFluentStepBuilder Given(Func step, string title); /// @@ -154,24 +176,34 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder Given(Expression> action); + [Pure] IFluentStepBuilder Given(string title); + [Pure] IFluentStepBuilder When(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder When(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder When(Expression> step); + [Pure] IFluentStepBuilder When(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder When(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder When(Expression> step); + [Pure] IFluentStepBuilder When(Action step, string title); + [Pure] IFluentStepBuilder When(Func step, string title); /// @@ -183,24 +215,34 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder When(Expression> action); + [Pure] IFluentStepBuilder When(string title); + [Pure] IFluentStepBuilder Then(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder Then(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder Then(Expression> step); + [Pure] IFluentStepBuilder Then(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder Then(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder Then(Expression> step); + [Pure] IFluentStepBuilder Then(Action step, string title); + [Pure] IFluentStepBuilder Then(Func step, string title); /// @@ -212,24 +254,34 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder Then(Expression> action); + [Pure] IFluentStepBuilder Then(string title); + [Pure] IFluentStepBuilder And(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder And(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder And(Expression> step); + [Pure] IFluentStepBuilder And(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder And(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder And(Expression> step); + [Pure] IFluentStepBuilder And(Action step, string title); + [Pure] IFluentStepBuilder And(Func step, string title); /// @@ -241,24 +293,34 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder And(Expression> action); + [Pure] IFluentStepBuilder And(string title); + [Pure] IFluentStepBuilder But(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder But(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder But(Expression> step); + [Pure] IFluentStepBuilder But(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder But(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder But(Expression> step); + [Pure] IFluentStepBuilder But(Action step, string title); + [Pure] IFluentStepBuilder But(Func step, string title); /// @@ -270,24 +332,34 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder But(Expression> action); + [Pure] IFluentStepBuilder But(string title); + [Pure] IFluentStepBuilder TearDownWith(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder TearDownWith(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder TearDownWith(Expression> step); + [Pure] IFluentStepBuilder TearDownWith(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder TearDownWith(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder TearDownWith(Expression> step); + [Pure] IFluentStepBuilder TearDownWith(Action step, string title); + [Pure] IFluentStepBuilder TearDownWith(Func step, string title); /// @@ -299,8 +371,10 @@ public interface IFluentStepBuilder where TScenario: class /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder TearDownWith(Expression> action); + [Pure] IFluentStepBuilder TearDownWith(string title); } @@ -319,8 +393,7 @@ public FluentStepBuilder(TScenario testObject) { TestObject = testObject; var existingContext = TestContext.GetContext(TestObject); - if (existingContext.FluentScanner == null) - existingContext.FluentScanner = Configurator.FluentScannerFactory.Create(testObject); + existingContext.FluentScanner ??= Configurator.FluentScannerFactory.Create(testObject); scanner = (FluentScanner) existingContext.FluentScanner; } diff --git a/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.tt b/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.tt index c3ad8552..25504a56 100644 --- a/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.tt +++ b/src/TestStack.BDDfy/Scanners/StepScanners/Fluent/FluentApi.tt @@ -17,6 +17,7 @@ }; #> using System; +using System.Diagnostics.Contracts; using System.Linq.Expressions; using System.Threading.Tasks; @@ -27,143 +28,164 @@ namespace TestStack.BDDfy { public static class FluentStepScannerExtensions { - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, string stepTextTemplate) + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + string stepTextTemplate) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, stepTextTemplate); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) + => new FluentStepBuilder(testObject).Given(step, stepTextTemplate); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step) + => new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, string stepTextTemplate) + => new FluentStepBuilder(testObject).Given(step); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + string stepTextTemplate) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, stepTextTemplate); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) + => new FluentStepBuilder(testObject).Given(step, stepTextTemplate); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> step) + => new FluentStepBuilder(testObject).Given(step, includeInputsInStepTitle); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> step) where TScenario: class - { - return new FluentStepBuilder(testObject).Given(step); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Action step, string title) + => new FluentStepBuilder(testObject).Given(step); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Action step, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).Given(step, title); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Func step, string title) + => new FluentStepBuilder(testObject).Given(step, title); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Func step, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).Given(step, title); - } - - public static IFluentStepBuilder Given(this TScenario testObject, Expression> action) + => new FluentStepBuilder(testObject).Given(step, title); + + public static IFluentStepBuilder Given( + this TScenario testObject, + Expression> action) where TScenario : class - { - return new FluentStepBuilder(testObject).Given(action); - } - - public static IFluentStepBuilder Given(this TScenario testObject, string title) + => new FluentStepBuilder(testObject).Given(action); + + public static IFluentStepBuilder Given( + this TScenario testObject, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).Given(title); - } + => new FluentStepBuilder(testObject).Given(title); - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, string stepTextTemplate) + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + string stepTextTemplate) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, stepTextTemplate); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) + => new FluentStepBuilder(testObject).When(step, stepTextTemplate); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step) + => new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, string stepTextTemplate) + => new FluentStepBuilder(testObject).When(step); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + string stepTextTemplate) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, stepTextTemplate); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step, bool includeInputsInStepTitle) + => new FluentStepBuilder(testObject).When(step, stepTextTemplate); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step, + bool includeInputsInStepTitle) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); - } - - public static IFluentStepBuilder When(this TScenario testObject, Expression> step) + => new FluentStepBuilder(testObject).When(step, includeInputsInStepTitle); + + public static IFluentStepBuilder When( + this TScenario testObject, + Expression> step) where TScenario: class - { - return new FluentStepBuilder(testObject).When(step); - } - - public static IFluentStepBuilder When(this TScenario testObject, Action step, string title) + => new FluentStepBuilder(testObject).When(step); + + public static IFluentStepBuilder When( + this TScenario testObject, + Action step, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).When(step, title); - } - - public static IFluentStepBuilder When(this TScenario testObject, Func step, string title) + => new FluentStepBuilder(testObject).When(step, title); + + public static IFluentStepBuilder When( + this TScenario testObject, + Func step, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).When(step, title); - } - - public static IFluentStepBuilder When(this TScenario testObject, string title) + => new FluentStepBuilder(testObject).When(step, title); + + public static IFluentStepBuilder When( + this TScenario testObject, + string title) where TScenario : class - { - return new FluentStepBuilder(testObject).When(title); - } + => new FluentStepBuilder(testObject).When(title); } public interface IFluentStepBuilder where TScenario: class { + [Pure] TScenario TestObject { get; } <# foreach (var stepType in steps) { #> + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step, string stepTextTemplate); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step, bool includeInputsInStepTitle); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> step); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Action step, string title); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Func step, string title); /// @@ -175,8 +197,10 @@ namespace TestStack.BDDfy /// | Do Other | /// Search for ExampleAction on the BDDfy wiki for more information /// + [Pure] IFluentStepBuilder <#=stepType.Item1#>(Expression> action); + [Pure] IFluentStepBuilder <#=stepType.Item1#>(string title); <# } From e1cfdfefa40f72d9352697960ace6a42e15c28bc Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 28 May 2026 14:17:08 +0100 Subject: [PATCH 6/6] remove tests that did not add any value and where causing other tests to fail randomly --- .../CanRunAsyncSteps.cs | 12 ++++------ .../CanRunAsyncVoidSteps.cs | 18 -------------- .../Processors/AsyncTestSyncContext.cs | 24 +++++++++++++------ 3 files changed, 21 insertions(+), 33 deletions(-) delete mode 100644 src/Samples/TestStack.BDDfy.Samples/CanRunAsyncVoidSteps.cs diff --git a/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncSteps.cs b/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncSteps.cs index 47516cc1..194c27ca 100644 --- a/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncSteps.cs +++ b/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncSteps.cs @@ -7,7 +7,7 @@ namespace TestStack.BDDfy.Samples { public class CanRunAsyncSteps { - private Sut _sut; + private object _sut; internal async void GivenSomeAsyncSetup() { @@ -25,23 +25,19 @@ internal async void AndThenBddfyShouldCaptureExceptionsThrownInAsyncMethod() throw new Exception("Exception in async void method!!"); } - private async Task CreateSut() + private static async Task CreateSut() { await Task.Delay(500); - return new Sut(); + return new object(); } [Fact] public void Run() { var engine = this.LazyBDDfy(); - var exception = Should.Throw(() => engine.Run()); + var exception = Should.Throw(engine.Run); exception.Message.ShouldBe("Exception in async void method!!"); } - - internal class Sut - { - } } } \ No newline at end of file diff --git a/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncVoidSteps.cs b/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncVoidSteps.cs deleted file mode 100644 index 36afc1e5..00000000 --- a/src/Samples/TestStack.BDDfy.Samples/CanRunAsyncVoidSteps.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Threading.Tasks; -using TestStack.BDDfy.Configuration; -using Xunit; - -namespace TestStack.BDDfy.Samples -{ - public class CanRunAsyncVoidSteps - { - [Fact] - internal void Run() => this.BDDfy(); - internal void SetUp() => Configurator.AsyncVoidSupportEnabled = false; - internal void TearDown() => Configurator.AsyncVoidSupportEnabled = true; - - internal async void GivenNonAsyncStep() => await Task.CompletedTask; - internal async void WhenSomethingHappens() => await Task.CompletedTask; - internal async void ThenAssertSomething() => await Task.CompletedTask; - } -} \ No newline at end of file diff --git a/src/TestStack.BDDfy/Processors/AsyncTestSyncContext.cs b/src/TestStack.BDDfy/Processors/AsyncTestSyncContext.cs index ecdbfe62..bab669be 100644 --- a/src/TestStack.BDDfy/Processors/AsyncTestSyncContext.cs +++ b/src/TestStack.BDDfy/Processors/AsyncTestSyncContext.cs @@ -8,21 +8,26 @@ namespace TestStack.BDDfy /// internal class AsyncTestSyncContext : SynchronizationContext { - readonly ManualResetEvent _event = new(initialState: true); + readonly object _lock = new(); Exception _exception; int _operationCount; public override void OperationCompleted() { - var result = Interlocked.Decrement(ref _operationCount); - if (result == 0) - _event.Set(); + lock (_lock) + { + _operationCount--; + if (_operationCount == 0) + Monitor.PulseAll(_lock); + } } public override void OperationStarted() { - Interlocked.Increment(ref _operationCount); - _event.Reset(); + lock (_lock) + { + _operationCount++; + } } public override void Post(SendOrPostCallback d, object state) @@ -59,7 +64,12 @@ public override void Send(SendOrPostCallback d, object state) public Exception WaitForCompletion() { - _event.WaitOne(); + lock (_lock) + { + while (_operationCount > 0) + Monitor.Wait(_lock); + } + return _exception; } }