Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(context): reduce responsibilities taken by Context, ClassContext, ExampleBase #193

Merged
merged 27 commits into from
Jun 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
26863fe
refactor(context): use dedicated classes to build and run class/conte…
May 19, 2017
3a9a864
fix(context): afterAll hook is incorrectly bound to after hook
May 19, 2017
01a0bdc
refactor(context): expose wrappers to set hooks
May 19, 2017
882b141
refactor(context): move beforeAll/afterAll exceptions to dedicated cl…
GiuseppePiscopo May 19, 2017
c2c622c
refactor(context): reuse `RunAndHandleException`
GiuseppePiscopo May 19, 2017
e99360b
refactor(afterAll): reuse checks from beforeAll chain
GiuseppePiscopo May 19, 2017
f8d6eb7
refactor(capturing): reducing capturing area to ease adding debug logs
GiuseppePiscopo May 20, 2017
4926a2a
refactor(context): move before/act/after exceptions to dedicated classes
GiuseppePiscopo May 20, 2017
4e4bac6
refactor(hooks): extract parts common to all hooks
GiuseppePiscopo May 20, 2017
69a4b60
chore(context): rename some nspec instances
GiuseppePiscopo May 20, 2017
ea40e43
refactor(hooks): extract building and running class/context hooks
GiuseppePiscopo May 20, 2017
c185e97
refactor(hooks): turn method selectors into fields
GiuseppePiscopo May 21, 2017
9d71a89
refactor(hooks): generalize running all hooks
GiuseppePiscopo May 21, 2017
cb4ba49
refactor(hooks): extract hooks traversing ancestors
GiuseppePiscopo May 21, 2017
bfab7ff
refactor(context): example knows how to add itself to context
GiuseppePiscopo May 21, 2017
4c8bc1c
refactor(context): let beforeAll/afterAll traverse exceptions
GiuseppePiscopo May 21, 2017
87905d6
refactor(hooks): move helper methods to dedicated class
GiuseppePiscopo May 21, 2017
fad71b6
refactor(hooks): streamline access to exceptions
GiuseppePiscopo May 21, 2017
73d3574
refactor(example): protect access to Pending property
GiuseppePiscopo May 21, 2017
37d4277
refactor(context): move to dedicated class the 3 phases or running an…
GiuseppePiscopo May 21, 2017
86d2e95
refactor(domain): protect access to context/example properties
GiuseppePiscopo May 21, 2017
1571710
refactor(context): simplify when spec constructor throws
GiuseppePiscopo May 22, 2017
acdfad8
Merge branch master
GiuseppePiscopo May 24, 2017
d5567a8
fix(hooks): mixed sync/async error message lacks hook name
GiuseppePiscopo Jun 12, 2017
cb5efe7
refactor(hooks): invoking hooks in direct/reverse order is easier to …
GiuseppePiscopo Jun 12, 2017
06ba488
chore(style): clean up unused imports
GiuseppePiscopo Jun 12, 2017
ffee444
refactor(hooks): simplify building sync/async method level hooks
GiuseppePiscopo Jun 12, 2017
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
20 changes: 20 additions & 0 deletions sln/src/NSpec/Domain/ActChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace NSpec.Domain
{
public class ActChain : TraversingHookChain
{
protected override bool CanRun(nspec instance)
{
return context.BeforeAllChain.AnyThrew()
? false
: !context.BeforeChain.AnyThrew();
}

public ActChain(Context context, Conventions conventions)
: base(context, "act", "actAsync", "act_each")
{
methodSelector = conventions.GetMethodLevelAct;
asyncMethodSelector = conventions.GetAsyncMethodLevelAct;
chainSelector = c => c.ActChain;
}
}
}
34 changes: 34 additions & 0 deletions sln/src/NSpec/Domain/AfterAllChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;

namespace NSpec.Domain
{
public class AfterAllChain : HookChainBase
{
protected override bool CanRun(nspec instance)
{
return context.BeforeAllChain.AncestorsThrew()
? false
: context.AnyUnfilteredExampleInSubTree(instance);
}

public bool AnyThrew()
{
return (AnyException() != null);
}

public override Exception AnyException()
{
// when hook chain is NOT traversed, build up exceptions along ancestor chain

// give precedence to Exception closer in the chain
return Exception ?? context.Parent?.AfterAllChain.AnyException();
}

public AfterAllChain(Context context, Conventions conventions)
: base(context, "afterAll", "afterAllAsync", "after_all", reversed: true)
{
methodSelector = conventions.GetMethodLevelAfterAll;
asyncMethodSelector = conventions.GetAsyncMethodLevelAfterAll;
}
}
}
18 changes: 18 additions & 0 deletions sln/src/NSpec/Domain/AfterChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace NSpec.Domain
{
public class AfterChain : TraversingHookChain
{
protected override bool CanRun(nspec instance)
{
return !context.BeforeAllChain.AnyThrew();
}

public AfterChain(Context context, Conventions conventions)
: base(context, "after", "afterAsync", "after_each", reversed: true)
{
methodSelector = conventions.GetMethodLevelAfter;
asyncMethodSelector = conventions.GetAsyncMethodLevelAfter;
chainSelector = c => c.AfterChain;
}
}
}
3 changes: 0 additions & 3 deletions sln/src/NSpec/Domain/AsyncActionRegister.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NSpec.Domain
Expand Down
7 changes: 1 addition & 6 deletions sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace NSpec.Domain
{
Expand Down
4 changes: 0 additions & 4 deletions sln/src/NSpec/Domain/AsyncMismatchException.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NSpec.Domain
{
Expand Down
39 changes: 39 additions & 0 deletions sln/src/NSpec/Domain/BeforeAllChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;

namespace NSpec.Domain
{
public class BeforeAllChain : HookChainBase
{
protected override bool CanRun(nspec instance)
{
return AncestorsThrew()
? false
: context.AnyUnfilteredExampleInSubTree(instance);
}

public bool AnyThrew()
{
return (Exception != null || AncestorsThrew());
}

public bool AncestorsThrew()
{
return (context.Parent?.BeforeAllChain.AnyThrew() ?? false);
}

public override Exception AnyException()
{
// when hook chain is NOT traversed, build up exceptions along ancestor chain

// give precedence to Exception farther up in the chain
return context.Parent?.BeforeAllChain.AnyException() ?? Exception;
}

public BeforeAllChain(Context context, Conventions conventions)
: base(context, "beforeAll", "beforeAllAsync", "before_all")
{
methodSelector = conventions.GetMethodLevelBeforeAll;
asyncMethodSelector = conventions.GetAsyncMethodLevelBeforeAll;
}
}
}
18 changes: 18 additions & 0 deletions sln/src/NSpec/Domain/BeforeChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace NSpec.Domain
{
public class BeforeChain : TraversingHookChain
{
protected override bool CanRun(nspec instance)
{
return !context.BeforeAllChain.AnyThrew();
}

public BeforeChain(Context context, Conventions conventions)
: base(context, "before", "beforeAsync", "before_each")
{
methodSelector = conventions.GetMethodLevelBefore;
asyncMethodSelector = conventions.GetAsyncMethodLevelBefore;
chainSelector = c => c.BeforeChain;
}
}
}
135 changes: 19 additions & 116 deletions sln/src/NSpec/Domain/ClassContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NSpec.Domain.Extensions;

Expand All @@ -10,15 +9,15 @@ public class ClassContext : Context
{
public override void Build(nspec unused = null)
{
BuildMethodLevelBefore();
BeforeAllChain.BuildMethodLevel(classHierarchy);

BuildMethodLevelBeforeAll();
BeforeChain.BuildMethodLevel(classHierarchy);

BuildMethodLevelAct();
ActChain.BuildMethodLevel(classHierarchy);

BuildMethodLevelAfter();
AfterChain.BuildMethodLevel(classHierarchy);

BuildMethodLevelAfterAll();
AfterAllChain.BuildMethodLevel(classHierarchy);

try
{
Expand Down Expand Up @@ -46,96 +45,6 @@ public override bool IsSub(Type baseType)
return baseType == SpecType;
}

IEnumerable<MethodInfo> GetMethodsFromHierarchy(Func<Type, MethodInfo> methodAccessor)
{
return classHierarchyToClass.Select(methodAccessor).Where(mi => mi != null);
}

void BuildMethodLevelBefore()
{
var befores = GetMethodsFromHierarchy(conventions.GetMethodLevelBefore).ToList();

if (befores.Count > 0)
{
BeforeInstance = instance => befores.Do(b => b.Invoke(instance, null));
}

var asyncBefores = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelBefore).ToList();

if (asyncBefores.Count > 0)
{
BeforeInstanceAsync = instance => asyncBefores.Do(b => new AsyncMethodLevelBefore(b).Run(instance));
}
}

void BuildMethodLevelBeforeAll()
{
var beforeAlls = GetMethodsFromHierarchy(conventions.GetMethodLevelBeforeAll).ToList();

if (beforeAlls.Count > 0)
{
BeforeAllInstance = instance => beforeAlls.Do(a => a.Invoke(instance, null));
}

var asyncBeforeAlls = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelBeforeAll).ToList();

if (asyncBeforeAlls.Count > 0)
{
BeforeAllInstanceAsync = instance => asyncBeforeAlls.Do(b => new AsyncMethodLevelBeforeAll(b).Run(instance));
}
}

void BuildMethodLevelAct()
{
var acts = GetMethodsFromHierarchy(conventions.GetMethodLevelAct).ToList();

if (acts.Count > 0)
{
ActInstance = instance => acts.Do(a => a.Invoke(instance, null));
}

var asyncActs = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAct).ToList();

if (asyncActs.Count > 0)
{
ActInstanceAsync = instance => asyncActs.Do(a => new AsyncMethodLevelAct(a).Run(instance));
}
}

void BuildMethodLevelAfter()
{
var afters = GetMethodsFromHierarchy(conventions.GetMethodLevelAfter).Reverse().ToList();

if (afters.Count > 0)
{
AfterInstance = instance => afters.Do(a => a.Invoke(instance, null));
}

var asyncAfters = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAfter).Reverse().ToList();

if (asyncAfters.Count > 0)
{
AfterInstanceAsync = instance => asyncAfters.Do(a => new AsyncMethodLevelAfter(a).Run(instance));
}
}

void BuildMethodLevelAfterAll()
{
var afterAlls = GetMethodsFromHierarchy(conventions.GetMethodLevelAfterAll).Reverse().ToList();

if (afterAlls.Count > 0)
{
AfterAllInstance = instance => afterAlls.Do(a => a.Invoke(instance, null));
}

var asyncAfterAlls = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAfterAll).Reverse().ToList();

if (asyncAfterAlls.Count > 0)
{
AfterAllInstanceAsync = instance => asyncAfterAlls.Do(a => new AsyncMethodLevelAfterAll(a).Run(instance));
}
}

void AddFailingExample(Exception targetEx)
{
var reportedEx = (targetEx.InnerException != null)
Expand All @@ -149,6 +58,9 @@ void AddFailingExample(Exception targetEx)

var failingExample = new Example(exampleName, action: emptyAction)
{
// flag the one and only failing example as being run;
// nothing else is needed: no parents, no childs, no before/after hooks
HasRun = true,
Exception = new ContextBareCodeException(reportedEx),
};

Expand All @@ -157,38 +69,29 @@ void AddFailingExample(Exception targetEx)

public override void Run(bool failFast, nspec instance = null, bool recurse = true)
{
if (cantCreateInstance)
{
// flag the one and only failing example as being run;
// nothing else is needed: no parents, no childs, no before/after hooks
Examples.Single().HasRun = true;
}
else
{
base.Run(failFast, instance, recurse);
}
if (cantCreateInstance) return;

base.Run(failFast, instance, recurse);
}

public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = null, string tags = null)
: base(type.CleanName(), tags)
: base(type.CleanName(), tags, false, conventions)
{
this.SpecType = type;

this.conventions = conventions ?? new DefaultConventions().Initialize();

this.tagsFilter = tagsFilter;

if (type != typeof(nspec))
{
classHierarchyToClass.AddRange(type.GetAbstractBaseClassChainWithClass());
}
this.classHierarchy = (type == typeof(nspec))
? new List<Type>()
: new List<Type>(type.GetAbstractBaseClassChainWithClass());

cantCreateInstance = false;
}

public Type SpecType;

Tags tagsFilter;
List<Type> classHierarchyToClass = new List<Type>();
Conventions conventions;
bool cantCreateInstance = false;
List<Type> classHierarchy;
bool cantCreateInstance;
}
}
Loading