Skip to content

Commit c04f840

Browse files
committed
Merge pull request #32 from JakeGinnivan/AsyncSupport
Async support
2 parents f8fcc7c + f57b702 commit c04f840

20 files changed

+487
-22
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#if !NET35
2+
using System;
3+
using System.Threading.Tasks;
4+
using NUnit.Framework;
5+
6+
namespace TestStack.BDDfy.Samples
7+
{
8+
public class AsyncExample
9+
{
10+
private Sut _sut;
11+
12+
public async void GivenSomeAsyncSetup()
13+
{
14+
_sut = await CreateSut();
15+
}
16+
17+
public void ThenBddfyHasWaitedForThatSetupToCompleteBeforeContinuing()
18+
{
19+
Assert.NotNull(_sut);
20+
}
21+
22+
public async Task AndThenBddfyShouldCaptureExceptionsThrownInAsyncMethod()
23+
{
24+
await Task.Yield();
25+
throw new Exception("Exception in async void method!!");
26+
}
27+
28+
private async Task<Sut> CreateSut()
29+
{
30+
await Task.Delay(500);
31+
return new Sut();
32+
}
33+
34+
[Test]
35+
public void Run()
36+
{
37+
var engine = this.LazyBDDfy();
38+
var exception = Assert.Throws<Exception>(() => engine.Run());
39+
Assert.AreEqual("Exception in async void method!!", exception.Message);
40+
}
41+
42+
internal class Sut
43+
{
44+
}
45+
}
46+
}
47+
#endif

Samples/TestStack.BDDfy.Samples/CustomTextReporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ static CustomTextReporter()
4444
public void Process(Story story)
4545
{
4646
// use this report only for tic tac toe stories
47-
if (!story.MetaData.Type.Name.Contains("TicTacToe"))
47+
if (story.MetaData == null || !story.MetaData.Type.Name.Contains("TicTacToe"))
4848
return;
4949

5050
var scenario = story.Scenarios.First();

Samples/TestStack.BDDfy.Samples/TestStack.BDDfy.Samples.csproj

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,45 @@
1010
<AppDesignerFolder>Properties</AppDesignerFolder>
1111
<RootNamespace>TestStack.BDDfy.Samples</RootNamespace>
1212
<AssemblyName>TestStack.BDDfy.Samples</AssemblyName>
13-
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13+
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
1414
<FileAlignment>512</FileAlignment>
1515
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
1616
<RestorePackages>true</RestorePackages>
17+
<TargetFrameworkProfile />
1718
</PropertyGroup>
1819
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1920
<DebugSymbols>true</DebugSymbols>
2021
<DebugType>full</DebugType>
2122
<Optimize>false</Optimize>
2223
<OutputPath>bin\Debug\</OutputPath>
23-
<DefineConstants>DEBUG;TRACE</DefineConstants>
24+
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
2425
<ErrorReport>prompt</ErrorReport>
2526
<WarningLevel>4</WarningLevel>
27+
<Prefer32Bit>false</Prefer32Bit>
2628
</PropertyGroup>
2729
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
2830
<DebugType>pdbonly</DebugType>
2931
<Optimize>true</Optimize>
3032
<OutputPath>bin\Release\</OutputPath>
31-
<DefineConstants>TRACE</DefineConstants>
33+
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
3234
<ErrorReport>prompt</ErrorReport>
3335
<WarningLevel>4</WarningLevel>
36+
<Prefer32Bit>false</Prefer32Bit>
3437
</PropertyGroup>
3538
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNET35|AnyCPU'">
3639
<DebugType>pdbonly</DebugType>
3740
<Optimize>true</Optimize>
3841
<OutputPath>bin\Release\</OutputPath>
39-
<DefineConstants>TRACE</DefineConstants>
42+
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
4043
<ErrorReport>prompt</ErrorReport>
4144
<WarningLevel>4</WarningLevel>
45+
<Prefer32Bit>false</Prefer32Bit>
4246
</PropertyGroup>
4347
<ItemGroup>
4448
<Folder Include="Properties\" />
4549
</ItemGroup>
4650
<ItemGroup>
51+
<Compile Include="AsyncExample.cs" />
4752
<Compile Include="Atm\AccountHasInsufficientFund.cs" />
4853
<Compile Include="Atm\Atm.cs" />
4954
<Compile Include="Atm\AccountHolderWithdrawsCash.cs" />
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#if !NET35
2+
using System;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
using NUnit.Framework;
6+
using TestStack.BDDfy.Scanners.StepScanners;
7+
8+
namespace TestStack.BDDfy.Tests.Scanner
9+
{
10+
[TestFixture]
11+
public class WhenStepScannerFactoryAsyncMethods
12+
{
13+
[Test]
14+
public void CallingAsyncTaskWhichThrowsIsObservedAndRethrown()
15+
{
16+
var stepAction = StepActionFactory.GetStepAction<SomeScenario>(o => AsyncTaskMethod(o));
17+
18+
Assert.Throws<ArgumentException>(()=>stepAction(new SomeScenario()));
19+
}
20+
21+
[Test]
22+
public void CallingAsyncVoidWhichThrowsIsObservedAndRethrown()
23+
{
24+
var stepAction = StepActionFactory.GetStepAction<SomeScenario>(s=>AsyncVoidMethod(s));
25+
26+
Assert.Throws<ArgumentException>(() => stepAction(new SomeScenario()));
27+
}
28+
29+
[Test]
30+
public void InvokingAsyncTaskWhichThrowsIsObservedAndRethrown()
31+
{
32+
var methodInfo = typeof(WhenStepScannerFactoryAsyncMethods).GetMethod("AsyncVoidMethod", BindingFlags.Instance | BindingFlags.NonPublic);
33+
var stepAction = StepActionFactory.GetStepAction(methodInfo, new object[] { new SomeScenario() });
34+
35+
Assert.Throws<ArgumentException>(() => stepAction(this));
36+
}
37+
38+
[Test]
39+
public void InvokingAsyncVoidWhichThrowsIsObservedAndRethrown()
40+
{
41+
var methodInfo = typeof(WhenStepScannerFactoryAsyncMethods).GetMethod("AsyncTaskMethod", BindingFlags.Instance | BindingFlags.NonPublic);
42+
var stepAction = StepActionFactory.GetStepAction(methodInfo, new object[] { new SomeScenario() });
43+
44+
Assert.Throws<ArgumentException>(() => stepAction(this));
45+
}
46+
47+
private async void AsyncVoidMethod(SomeScenario someScenario)
48+
{
49+
await Task.Yield();
50+
throw new ArgumentException();
51+
}
52+
53+
private async Task AsyncTaskMethod(SomeScenario obj)
54+
{
55+
await Task.Yield();
56+
throw new ArgumentException();
57+
}
58+
59+
private class SomeScenario
60+
{
61+
}
62+
}
63+
}
64+
#endif

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,31 @@
1010
<AppDesignerFolder>Properties</AppDesignerFolder>
1111
<RootNamespace>TestStack.BDDfy.Tests</RootNamespace>
1212
<AssemblyName>TestStack.BDDfy.Tests</AssemblyName>
13-
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13+
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
1414
<FileAlignment>512</FileAlignment>
1515
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
1616
<RestorePackages>true</RestorePackages>
17+
<TargetFrameworkProfile />
1718
</PropertyGroup>
1819
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1920
<DebugSymbols>true</DebugSymbols>
2021
<DebugType>full</DebugType>
2122
<Optimize>false</Optimize>
2223
<OutputPath>bin\Debug\</OutputPath>
23-
<DefineConstants>DEBUG;TRACE</DefineConstants>
24+
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
2425
<ErrorReport>prompt</ErrorReport>
2526
<WarningLevel>4</WarningLevel>
27+
<Prefer32Bit>false</Prefer32Bit>
2628
</PropertyGroup>
2729
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
2830
<DebugType>pdbonly</DebugType>
2931
<Optimize>true</Optimize>
3032
<OutputPath>bin\Release\</OutputPath>
31-
<DefineConstants>TRACE</DefineConstants>
33+
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
3234
<ErrorReport>prompt</ErrorReport>
3335
<WarningLevel>4</WarningLevel>
3436
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
37+
<Prefer32Bit>false</Prefer32Bit>
3538
</PropertyGroup>
3639
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNET35|AnyCPU'">
3740
<DebugType>pdbonly</DebugType>
@@ -40,6 +43,7 @@
4043
<DefineConstants>TRACE</DefineConstants>
4144
<ErrorReport>prompt</ErrorReport>
4245
<WarningLevel>4</WarningLevel>
46+
<Prefer32Bit>false</Prefer32Bit>
4347
</PropertyGroup>
4448
<ItemGroup>
4549
<Reference Include="NSubstitute, Version=1.6.1.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca, processorArchitecture=MSIL">
@@ -62,6 +66,7 @@
6266
<Compile Include="Arguments\ArgumentsProvidedForGiven.cs" />
6367
<Compile Include="Arguments\ArgumentsProvidedForThen.cs" />
6468
<Compile Include="Arguments\MultipleArgumentsProvidedForTheSameStep.cs" />
69+
<Compile Include="Scanner\WhenStepScannerFactoryAsyncMethods.cs" />
6570
<Compile Include="Configuration\CustomProcessor.cs" />
6671
<Compile Include="Configuration\BatchProcessorsTests.cs" />
6772
<Compile Include="Configuration\ProcessorPipelineTests.cs" />

TestStack.BDDfy.build

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
Solution redirects. Every VS project normally knows how to support these targets
4444
-->
4545
<Target Name="Build">
46-
<MSBuild Projects="$(ProjectName).sln" Targets="Build" Properties="Configuration=$(Configuration)$(BuildFramework);DefineConstants=$(BuildConstants);MSBuildTargets=$(BuildTargets);TargetFrameworkVersion=$(TargetFrameworkVersion);TargetFrameworkProfile=$(TargetFrameworkProfile)"/>
46+
<MSBuild Projects="Samples\TestStack.BDDfy.Samples\TestStack.BDDfy.Samples.csproj" Targets="Build" Properties="Configuration=$(Configuration)$(BuildFramework);DefineConstants=$(BuildConstants);MSBuildTargets=$(BuildTargets);nowarn=1685"/>
47+
<MSBuild Projects="TestStack.BDDfy.Tests\TestStack.BDDfy.Tests.csproj" Targets="Build" Properties="Configuration=$(Configuration)$(BuildFramework);DefineConstants=$(BuildConstants);MSBuildTargets=$(BuildTargets);nowarn=1685"/>
48+
<MSBuild Projects="TestStack.BDDfy\TestStack.BDDfy.csproj" Targets="Build" Properties="Configuration=$(Configuration)$(BuildFramework);DefineConstants=$(BuildConstants);MSBuildTargets=$(BuildTargets);TargetFrameworkVersion=$(TargetFrameworkVersion);TargetFrameworkProfile=$(TargetFrameworkProfile)"/>
4749
</Target>
4850

4951
<Target Name="Clean">

TestStack.BDDfy/Processors/ExceptionProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public ProcessType ProcessType
7575
}
7676

7777
// http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx
78-
private static void PreserveStackTrace(Exception exception)
78+
internal static void PreserveStackTrace(Exception exception)
7979
{
8080
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
8181
BindingFlags.Instance | BindingFlags.NonPublic);

TestStack.BDDfy/Processors/TestRunner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void Process(Story story)
1818
if (scenario.ExecuteStep(executionStep) == StepExecutionResult.Passed)
1919
continue;
2020

21-
if(!executionStep.Asserts)
21+
if(!executionStep.Asserts)
2222
break;
2323
}
2424
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Threading;
3+
4+
namespace TestStack.BDDfy.Scanners.StepScanners
5+
{
6+
/// <summary>
7+
/// Implementation from xUnit 2.0
8+
/// </summary>
9+
internal class AsyncTestSyncContext : SynchronizationContext
10+
{
11+
readonly ManualResetEvent _event = new ManualResetEvent(initialState: true);
12+
Exception _exception;
13+
int _operationCount;
14+
15+
public override void OperationCompleted()
16+
{
17+
var result = Interlocked.Decrement(ref _operationCount);
18+
if (result == 0)
19+
_event.Set();
20+
}
21+
22+
public override void OperationStarted()
23+
{
24+
Interlocked.Increment(ref _operationCount);
25+
_event.Reset();
26+
}
27+
28+
public override void Post(SendOrPostCallback d, object state)
29+
{
30+
// The call to Post() may be the state machine signaling that an exception is
31+
// about to be thrown, so we make sure the operation count gets incremented
32+
// before the QUWI, and then decrement the count when the operation is done.
33+
OperationStarted();
34+
35+
ThreadPool.QueueUserWorkItem(s =>
36+
{
37+
try
38+
{
39+
Send(d, state);
40+
}
41+
finally
42+
{
43+
OperationCompleted();
44+
}
45+
});
46+
}
47+
48+
public override void Send(SendOrPostCallback d, object state)
49+
{
50+
try
51+
{
52+
d(state);
53+
}
54+
catch (Exception ex)
55+
{
56+
_exception = ex;
57+
}
58+
}
59+
60+
public Exception WaitForCompletion()
61+
{
62+
_event.WaitOne();
63+
return _exception;
64+
}
65+
}
66+
}

TestStack.BDDfy/Scanners/StepScanners/ExecutableAttribute/ExecutableAttributeStepScanner.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections.Generic;
32
using System.Linq;
43
using System.Reflection;
@@ -42,7 +41,7 @@ public IEnumerable<ExecutionStep> Scan(object testObject, MethodInfo candidateMe
4241
if (runStepWithArgsAttributes.Length == 0)
4342
{
4443
yield return
45-
new ExecutionStep(GetStepAction(candidateMethod), stepTitle, stepAsserts, executableAttribute.ExecutionOrder, true)
44+
new ExecutionStep(StepActionFactory.GetStepAction(candidateMethod, new object[0]), stepTitle, stepAsserts, executableAttribute.ExecutionOrder, true)
4645
{
4746
ExecutionSubOrder = executableAttribute.Order
4847
};
@@ -61,19 +60,14 @@ public IEnumerable<ExecutionStep> Scan(object testObject, MethodInfo candidateMe
6160
methodName = string.Format(executableAttribute.StepTitle, flatInput);
6261

6362
yield return
64-
new ExecutionStep(GetStepAction(candidateMethod, inputArguments), methodName, stepAsserts,
63+
new ExecutionStep(StepActionFactory.GetStepAction(candidateMethod, inputArguments), methodName, stepAsserts,
6564
executableAttribute.ExecutionOrder, true)
6665
{
6766
ExecutionSubOrder = executableAttribute.Order
6867
};
6968
}
7069
}
7170

72-
static Action<object> GetStepAction(MethodInfo methodinfo, object[] inputArguments = null)
73-
{
74-
return o => methodinfo.Invoke(o, inputArguments);
75-
}
76-
7771
private static bool IsAssertingByAttribute(MethodInfo method)
7872
{
7973
var attribute = GetExecutableAttribute(method);

0 commit comments

Comments
 (0)