From 27baa3888b467488d5ab0fa7658cd9ffc4b93fe1 Mon Sep 17 00:00:00 2001 From: Matthew Ryan Date: Wed, 8 Aug 2018 10:49:20 -0500 Subject: [PATCH 1/2] Add test case filter to RunOptions --- Source/Examples/Example/FailingSpecs.cs | 13 ++++ ...icationRunnerRunAssemblyWithFilterSpecs.cs | 61 +++++++++++++++++++ .../Explorers/AssemblyExplorerTests.cs | 7 ++- .../Runner/ContextFilter.cs | 22 +++++++ .../Runner/Impl/DefaultRunner.cs | 34 ++++++++++- .../Runner/RunOptions.cs | 16 ++++- .../Runner/SpecifiactionFilter.cs | 17 ++++++ appveyor.yml | 6 +- 8 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 Source/Examples/Example/FailingSpecs.cs create mode 100644 Source/Machine.Specifications.Specs/Runner/SpecificationRunnerRunAssemblyWithFilterSpecs.cs create mode 100644 Source/Machine.Specifications/Runner/ContextFilter.cs create mode 100644 Source/Machine.Specifications/Runner/SpecifiactionFilter.cs diff --git a/Source/Examples/Example/FailingSpecs.cs b/Source/Examples/Example/FailingSpecs.cs new file mode 100644 index 000000000..fba7c033d --- /dev/null +++ b/Source/Examples/Example/FailingSpecs.cs @@ -0,0 +1,13 @@ +using System; +using Machine.Specifications; + +namespace Example +{ + [Subject("example")] + public class when_context_with_passing_and_failing_clauses + { + It should_fail = () => throw new Exception(); + + It should_pass = () => { }; + } +} \ No newline at end of file diff --git a/Source/Machine.Specifications.Specs/Runner/SpecificationRunnerRunAssemblyWithFilterSpecs.cs b/Source/Machine.Specifications.Specs/Runner/SpecificationRunnerRunAssemblyWithFilterSpecs.cs new file mode 100644 index 000000000..c561bf3b2 --- /dev/null +++ b/Source/Machine.Specifications.Specs/Runner/SpecificationRunnerRunAssemblyWithFilterSpecs.cs @@ -0,0 +1,61 @@ + +using System.Linq; +using System.Reflection; + +using Example; + +using FluentAssertions; +using Machine.Specifications.Runner; +using Machine.Specifications.Runner.Impl; + +namespace Machine.Specifications.Specs.Runner +{ + [Subject("Specification Runner")] + public class when_running_a_context_while_filtering_out_failing_tests + { + static TestListener testListener; + + static DefaultRunner runner; + + Establish context = () => + { + testListener = new TestListener(); + var filters = new[] + { + new ContextFilter( + "Example.when_context_with_passing_and_failing_clauses", + new[] + { + new SpecifiactionFilter("should_pass"), + }) + }; + var runOptions = new RunOptions( + Enumerable.Empty(), + Enumerable.Empty(), + filters); + + runner = new DefaultRunner( + testListener, + runOptions); + }; + + Because of = + () => runner.RunAssemblyContainingType(); + + It should_succeed = + () => testListener.LastResult.Passed.Should().BeTrue(); + + It should_run_spec = + () => testListener.SpecCount.Should().Be(1); + } + + public static class RunnerExtensions + { + public static void RunAssemblyContainingType( + this DefaultRunner runner) + where T : new() + { + runner.RunAssembly(typeof(T).GetTypeInfo().Assembly); + } + } +} \ No newline at end of file diff --git a/Source/Machine.Specifications.Tests/Explorers/AssemblyExplorerTests.cs b/Source/Machine.Specifications.Tests/Explorers/AssemblyExplorerTests.cs index dc603abce..58f2af980 100644 --- a/Source/Machine.Specifications.Tests/Explorers/AssemblyExplorerTests.cs +++ b/Source/Machine.Specifications.Tests/Explorers/AssemblyExplorerTests.cs @@ -22,18 +22,19 @@ public override void BeforeEachTest() } [Test] - public void ShouldReturnThreeContexts() + public void ShouldReturnFourContexts() { - specifications.Count().Should().Be(3); + specifications.Count().Should().Be(4); } [Test] - public void ShouldReturnThreeContextsNamedCorrectly() + public void ShouldReturnFourContextsNamedCorrectly() { var names = specifications.Select(x => x.Name).OrderBy(x => x).ToList(); names.Should().BeEquivalentTo( new[] { + "when context with passing and failing clauses", "when a customer first views the account summary page", "when transferring between two accounts", "when transferring an amount larger than the balance of the from account" diff --git a/Source/Machine.Specifications/Runner/ContextFilter.cs b/Source/Machine.Specifications/Runner/ContextFilter.cs new file mode 100644 index 000000000..b99a6b0c5 --- /dev/null +++ b/Source/Machine.Specifications/Runner/ContextFilter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Machine.Specifications.Runner +{ +#if !NETSTANDARD + [Serializable] +#endif + public class ContextFilter + { + public ContextFilter(string name, IEnumerable specificFilters) + { + Name = name; + SpecificationFilters = specificFilters.ToList(); + } + + public string Name { get; } + + public IEnumerable SpecificationFilters { get; } + } +} \ No newline at end of file diff --git a/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs b/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs index 7c95fef5c..cf0f1626d 100644 --- a/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs +++ b/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs @@ -246,9 +246,37 @@ public static IEnumerable FilteredBy(this IEnumerable contexts if (options.Filters.Any()) { - var includeFilters = options.Filters; - - results = results.Where(x => includeFilters.Any(filter => StringComparer.OrdinalIgnoreCase.Equals(filter, x.Type.FullName))); + var includeFilters = options.Filters.ToList(); + + results = results.Where( + x => includeFilters.Any( + filter => StringComparer.OrdinalIgnoreCase.Equals( + filter.Name, + x.Type.FullName))) + .ToList(); + + foreach (var context in results) + { + ContextFilter contextFilter = includeFilters.Single( + x => StringComparer.OrdinalIgnoreCase.Equals( + x.Name, + context.Type.FullName)); + List specifiactionFilters = contextFilter.SpecificationFilters.ToList(); + if (!specifiactionFilters.Any()) + { + continue; + } + + var specsToRun = context.Specifications + .Where( + s => specifiactionFilters.Any( + sf => StringComparer.OrdinalIgnoreCase.Equals( + sf.Name, + s.FieldInfo.Name))); + context.Filter(specsToRun); + } + + results = results.Where(x => x.Specifications.Any()); } if (options.IncludeTags.Any()) diff --git a/Source/Machine.Specifications/Runner/RunOptions.cs b/Source/Machine.Specifications/Runner/RunOptions.cs index 80c4239da..5c93482cc 100644 --- a/Source/Machine.Specifications/Runner/RunOptions.cs +++ b/Source/Machine.Specifications/Runner/RunOptions.cs @@ -13,7 +13,7 @@ public class RunOptions { public IEnumerable IncludeTags { get; private set; } public IEnumerable ExcludeTags { get; private set; } - public IEnumerable Filters { get; private set; } + public IEnumerable Filters { get; private set; } public IEnumerable Contexts { get; private set; } public RunOptions(IEnumerable includeTags, IEnumerable excludeTags, IEnumerable filters) @@ -21,7 +21,17 @@ public RunOptions(IEnumerable includeTags, IEnumerable excludeTa { } + public RunOptions(IEnumerable includeTags, IEnumerable excludeTags, IEnumerable filters) + : this(includeTags, excludeTags, filters, Enumerable.Empty()) + { + } + public RunOptions(IEnumerable includeTags, IEnumerable excludeTags, IEnumerable filters, IEnumerable contexts) + : this(includeTags, excludeTags, filters.Select(x => new ContextFilter(x, new SpecifiactionFilter[0])), contexts) + { + } + + public RunOptions(IEnumerable includeTags, IEnumerable excludeTags, IEnumerable filters, IEnumerable contexts) { IncludeTags = includeTags; ExcludeTags = excludeTags; @@ -29,7 +39,7 @@ public RunOptions(IEnumerable includeTags, IEnumerable excludeTa Contexts = contexts; } - public static RunOptions Default { get { return new RunOptions(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); } } + public static RunOptions Default { get { return new RunOptions(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); } } public static RunOptions Parse(string runOptionsXml) { @@ -39,7 +49,7 @@ public static RunOptions Parse(string runOptionsXml) IEnumerable includeTags = Parse(document, "/runoptions/includetags/tag"); IEnumerable excludeTags = Parse(document, "/runoptions/excludetags/tag"); - IEnumerable filters = Parse(document, "/runoptions/filters/filter"); + IEnumerable filters = Parse(document, "/runoptions/filters/filter").Select(x => new ContextFilter(x, Enumerable.Empty())); IEnumerable contexts = Parse(document, "/runoptions/contexts/context"); return new RunOptions(includeTags, excludeTags, filters, contexts); diff --git a/Source/Machine.Specifications/Runner/SpecifiactionFilter.cs b/Source/Machine.Specifications/Runner/SpecifiactionFilter.cs new file mode 100644 index 000000000..bd33f4dfd --- /dev/null +++ b/Source/Machine.Specifications/Runner/SpecifiactionFilter.cs @@ -0,0 +1,17 @@ +using System; + +namespace Machine.Specifications.Runner +{ +#if !NETSTANDARD + [Serializable] +#endif + public class SpecifiactionFilter + { + public SpecifiactionFilter(string name) + { + Name = name; + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index b5425909e..5bbc3c95a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,14 @@ environment: - nuget_version: '0.12.0' + nuget_version: '0.13.0' nuget_prerelease: false - assembly_version: '0.12.0.0' + assembly_version: '0.13.0.0' image: Visual Studio 2017 deploy: - provider: GitHub description: | - * Track behavior field from context in behavior specification + * Add filtering for test cases to RunOptions on: appveyor_repo_tag: true From 1e2599ccd865d0bba7f24a33771a6d27cabf1f17 Mon Sep 17 00:00:00 2001 From: Matthew Ryan Date: Wed, 8 Aug 2018 16:34:35 -0500 Subject: [PATCH 2/2] Fix issue with not deferring exection of context - Iterating the contexts inside the filter was causing context to be created early. --- .../Runner/Impl/DefaultRunner.cs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs b/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs index cf0f1626d..0b08eee99 100644 --- a/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs +++ b/Source/Machine.Specifications/Runner/Impl/DefaultRunner.cs @@ -248,35 +248,24 @@ public static IEnumerable FilteredBy(this IEnumerable contexts { var includeFilters = options.Filters.ToList(); - results = results.Where( - x => includeFilters.Any( - filter => StringComparer.OrdinalIgnoreCase.Equals( - filter.Name, - x.Type.FullName))) - .ToList(); - - foreach (var context in results) - { - ContextFilter contextFilter = includeFilters.Single( - x => StringComparer.OrdinalIgnoreCase.Equals( - x.Name, - context.Type.FullName)); - List specifiactionFilters = contextFilter.SpecificationFilters.ToList(); - if (!specifiactionFilters.Any()) - { - continue; - } - - var specsToRun = context.Specifications - .Where( - s => specifiactionFilters.Any( - sf => StringComparer.OrdinalIgnoreCase.Equals( - sf.Name, - s.FieldInfo.Name))); - context.Filter(specsToRun); - } - - results = results.Where(x => x.Specifications.Any()); + results = results.Where(x => includeFilters.Any(filter => StringComparer.OrdinalIgnoreCase.Equals(filter.Name, x.Type.FullName))) + .Select(x => + { + ContextFilter contextFilter = includeFilters.Single(y => StringComparer.OrdinalIgnoreCase + .Equals(y.Name, x.Type.FullName)); + List specifiactionFilters = contextFilter.SpecificationFilters + .ToList(); + if (!specifiactionFilters.Any()) + { + return x; + } + + var specsToRun = x.Specifications + .Where(s => specifiactionFilters.Any(sf => StringComparer.OrdinalIgnoreCase.Equals(sf.Name, s.FieldInfo.Name))); + x.Filter(specsToRun); + return x; + }) + .Where(x => x.Specifications.Any()); } if (options.IncludeTags.Any())