Skip to content

Commit a01f400

Browse files
committed
using Xunit test output helper for logging
1 parent 35a104a commit a01f400

File tree

12 files changed

+296
-8
lines changed

12 files changed

+296
-8
lines changed

src/WorkflowCore.Testing/WorkflowCore.Testing.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@
1919
<ProjectReference Include="..\..\src\WorkflowCore\WorkflowCore.csproj" />
2020
</ItemGroup>
2121

22+
<ItemGroup>
23+
<Reference Include="xunit.abstractions">
24+
<HintPath>..\..\..\..\..\.nuget\packages\xunit.abstractions\2.0.3\lib\netstandard2.0\xunit.abstractions.dll</HintPath>
25+
</Reference>
26+
</ItemGroup>
27+
2228
</Project>

src/WorkflowCore.Testing/WorkflowTest.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.Extensions.Logging;
88
using WorkflowCore.Interface;
99
using WorkflowCore.Models;
10+
using Xunit.Abstractions;
1011

1112
namespace WorkflowCore.Testing
1213
{
@@ -18,11 +19,16 @@ public abstract class WorkflowTest<TWorkflow, TData> : IDisposable
1819
protected IPersistenceProvider PersistenceProvider;
1920
protected List<StepError> UnhandledStepErrors = new List<StepError>();
2021

21-
protected virtual void Setup()
22+
protected virtual void Setup(ITestOutputHelper testOutputHelper = null)
2223
{
2324
//setup dependency injection
2425
IServiceCollection services = new ServiceCollection();
25-
services.AddLogging();
26+
services.AddLogging(l => l.SetMinimumLevel(LogLevel.Trace));
27+
services.AddSingleton<ILoggerProvider>(p => new XUnitLoggerProvider(testOutputHelper));
28+
services.Add(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
29+
services.Add(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(XUnitLogger<>)));
30+
services.AddSingleton(sp => testOutputHelper);
31+
2632
ConfigureServices(services);
2733

2834
var serviceProvider = services.BuildServiceProvider();
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Text;
3+
using Microsoft.Extensions.Logging;
4+
using Xunit.Abstractions;
5+
6+
namespace WorkflowCore.Testing
7+
{
8+
internal class XUnitLogger : ILogger
9+
{
10+
private readonly ITestOutputHelper _testOutputHelper;
11+
private readonly string _categoryName;
12+
private readonly LoggerExternalScopeProvider _scopeProvider;
13+
14+
public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) =>
15+
new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), "");
16+
17+
public static ILogger<T> CreateLogger<T>(ITestOutputHelper testOutputHelper) =>
18+
new XUnitLogger<T>(testOutputHelper, new LoggerExternalScopeProvider());
19+
20+
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider,
21+
string categoryName)
22+
{
23+
_testOutputHelper = testOutputHelper;
24+
_scopeProvider = scopeProvider;
25+
_categoryName = categoryName;
26+
}
27+
28+
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
29+
30+
public IDisposable BeginScope<TState>(TState state) => _scopeProvider.Push(state);
31+
32+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
33+
Func<TState, Exception, string> formatter)
34+
{
35+
if (_testOutputHelper == null) return;
36+
var sb = new StringBuilder();
37+
sb.Append(DateTime.Now.ToString("HH:mm:ss.fff"))
38+
.Append(" ")
39+
.Append(GetLogLevelString(logLevel))
40+
.Append(" [").Append(_categoryName).Append("] ")
41+
.Append(formatter(state, exception));
42+
43+
if (exception != null)
44+
{
45+
sb.Append('\n').Append(exception);
46+
}
47+
48+
// Append scopes
49+
_scopeProvider.ForEachScope((scope, s) =>
50+
{
51+
s.Append("\n => ");
52+
s.Append(scope);
53+
}, sb);
54+
55+
_testOutputHelper.WriteLine(sb.ToString());
56+
}
57+
58+
private static string GetLogLevelString(LogLevel logLevel)
59+
{
60+
return logLevel.ToString().ToUpper();
61+
}
62+
}
63+
64+
internal sealed class XUnitLogger<T> : XUnitLogger, ILogger<T>
65+
{
66+
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider)
67+
: base(testOutputHelper, scopeProvider, typeof(T).FullName)
68+
{
69+
}
70+
}
71+
72+
internal sealed class XUnitLoggerProvider : ILoggerProvider
73+
{
74+
private readonly ITestOutputHelper _testOutputHelper;
75+
private readonly LoggerExternalScopeProvider _scopeProvider = new LoggerExternalScopeProvider();
76+
77+
public XUnitLoggerProvider(ITestOutputHelper testOutputHelper)
78+
{
79+
_testOutputHelper = testOutputHelper;
80+
}
81+
82+
public ILogger CreateLogger(string categoryName)
83+
{
84+
return new XUnitLogger(_testOutputHelper, _scopeProvider, categoryName);
85+
}
86+
87+
public void Dispose()
88+
{
89+
}
90+
}
91+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System;
2+
using FluentAssertions;
3+
using WorkflowCore.Interface;
4+
using WorkflowCore.Models;
5+
using WorkflowCore.Testing;
6+
using Xunit;
7+
using Xunit.Abstractions;
8+
9+
namespace WorkflowCore.IntegrationTests.Scenarios;
10+
11+
public class ApprovalScenario : WorkflowTest<ApprovalScenario.ParentWorkflow, ApprovalScenario.ApprovalInput>
12+
{
13+
public class ApprovalInput
14+
{
15+
public string Id { get; set; }
16+
public bool Approved { get; set; }
17+
public TimeSpan TimeSpan { get; set; }
18+
public string Message { get; set; }
19+
}
20+
21+
public class ParentWorkflow : IWorkflow<ApprovalInput>
22+
{
23+
public string Id => nameof(ParentWorkflow);
24+
25+
public int Version => 1;
26+
27+
public void Build(IWorkflowBuilder<ApprovalInput> builder)
28+
{
29+
builder
30+
.StartWith(context => ExecutionResult.Next())
31+
.SubWorkflow(nameof(ChildWorkflow))
32+
.If(data => data.Approved)
33+
.Do(then =>
34+
ExecutionResult.Outcome(1248));
35+
}
36+
}
37+
38+
public class ChildWorkflow : IWorkflow<ApprovalInput>
39+
{
40+
public string Id => nameof(ChildWorkflow);
41+
42+
public int Version => 1;
43+
44+
public void Build(IWorkflowBuilder<ApprovalInput> builder)
45+
{
46+
builder
47+
.StartWith(context => ExecutionResult.Next())
48+
.Parallel()
49+
.Do(then
50+
=> then
51+
.Delay(i => i.TimeSpan)
52+
.Then(ctx => ExecutionResult.Outcome(false))
53+
)
54+
.Do(then
55+
=> then
56+
.WaitFor("Approved", e => e.Id, data => DateTime.Now)
57+
.Output(i => i.Approved, step => true) // step.EventData)
58+
//.Output(i => i.Message, step => step.EventData)
59+
.Then(ctx => ExecutionResult.Outcome(true))
60+
)
61+
.Join(JoinMode.WaitAny)
62+
.EndWorkflow();
63+
}
64+
}
65+
66+
public ApprovalScenario(ITestOutputHelper testOutputHelper)
67+
{
68+
Setup(testOutputHelper);
69+
}
70+
71+
[Theory]
72+
[InlineData(true)]
73+
[InlineData(false)]
74+
public void Scenario(bool approved)
75+
{
76+
Host.Registry.RegisterWorkflow(new ChildWorkflow());
77+
78+
var eventKey = Guid.NewGuid().ToString();
79+
var workflowId = StartWorkflow(new ApprovalInput
80+
{
81+
Id = eventKey,
82+
TimeSpan = TimeSpan.FromMinutes(10)
83+
});
84+
85+
WaitForEventSubscription("Approved", workflowId, TimeSpan.FromSeconds(5));
86+
UnhandledStepErrors.Should().BeEmpty();
87+
88+
Host.PublishEvent("Approved", workflowId, new
89+
{
90+
Approved = approved,
91+
Message = "message"
92+
});
93+
94+
WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(20));
95+
96+
System.Threading.Thread.Sleep(2000);
97+
98+
UnhandledStepErrors.Count.Should().Be(0);
99+
GetStatus(workflowId).Should().Be(WorkflowStatus.Complete);
100+
GetData(workflowId).Approved.Should().Be(approved);
101+
}
102+
103+
[Fact]
104+
public void Timeout()
105+
{
106+
Host.Registry.RegisterWorkflow(new ChildWorkflow());
107+
108+
var workflowId = StartWorkflow(new ApprovalInput
109+
{
110+
Id = Guid.NewGuid().ToString(),
111+
TimeSpan = TimeSpan.FromSeconds(5)
112+
});
113+
WaitForEventSubscription("Approved", workflowId, TimeSpan.FromSeconds(2));
114+
WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(10));
115+
116+
UnhandledStepErrors.Count.Should().Be(0);
117+
GetStatus(workflowId).Should().Be(WorkflowStatus.Complete);
118+
GetData(workflowId).Approved.Should().BeFalse();
119+
}
120+
}

test/WorkflowCore.IntegrationTests/Scenarios/DelayScenario.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Xunit;
55
using FluentAssertions;
66
using WorkflowCore.Testing;
7+
using Xunit.Abstractions;
78

89
namespace WorkflowCore.IntegrationTests.Scenarios
910
{
@@ -31,9 +32,9 @@ public class MyDataClass
3132

3233
public class DelayScenario : WorkflowTest<DelayWorkflow, DelayWorkflow.MyDataClass>
3334
{
34-
public DelayScenario()
35+
public DelayScenario(ITestOutputHelper testOutputHelper)
3536
{
36-
Setup();
37+
Setup(testOutputHelper);
3738
}
3839

3940
[Fact]

test/WorkflowCore.Tests.MongoDB/Scenarios/MongoDelayScenario.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
using MongoDB.Bson.Serialization;
44
using WorkflowCore.IntegrationTests.Scenarios;
55
using Xunit;
6+
using Xunit.Abstractions;
67

78
namespace WorkflowCore.Tests.MongoDB.Scenarios
89
{
910
[Collection("Mongo collection")]
1011
public class MongoDelayScenario : DelayScenario
1112
{
12-
public MongoDelayScenario() : base()
13+
public MongoDelayScenario(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
1314
{
1415
BsonClassMap.RegisterClassMap<DelayWorkflow.MyDataClass>(map => map.AutoMap());
1516
}

test/WorkflowCore.Tests.MySQL/Scenarios/MysqlDelayScenario.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
using System;
33
using WorkflowCore.IntegrationTests.Scenarios;
44
using Xunit;
5+
using Xunit.Abstractions;
56

67
namespace WorkflowCore.Tests.MySQL.Scenarios
78
{
89
[Collection("Mysql collection")]
910
public class MysqlDelayScenario : DelayScenario
1011
{
12+
public MysqlDelayScenario(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
13+
{
14+
}
15+
1116
protected override void ConfigureServices(IServiceCollection services)
1217
{
1318
services.AddWorkflow(cfg =>

test/WorkflowCore.Tests.Oracle/Scenarios/OracleDelayScenario.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
using WorkflowCore.Tests.Oracle;
66

77
using Xunit;
8+
using Xunit.Abstractions;
89

910
namespace WorkflowCore.Tests.Oracle.Scenarios
1011
{
1112
[Collection("Oracle collection")]
1213
public class OracleDelayScenario : DelayScenario
13-
{
14+
{
15+
public OracleDelayScenario(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
16+
{
17+
}
18+
1419
protected override void ConfigureServices(IServiceCollection services)
1520
{
1621
services.AddWorkflow(cfg =>

test/WorkflowCore.Tests.PostgreSQL/Scenarios/PostgresDelayScenario.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
using Microsoft.Extensions.DependencyInjection;
33
using WorkflowCore.IntegrationTests.Scenarios;
44
using Xunit;
5+
using Xunit.Abstractions;
56

67
namespace WorkflowCore.Tests.PostgreSQL.Scenarios
78
{
89
[Collection("Postgres collection")]
910
public class PostgresDelayScenario : DelayScenario
10-
{
11+
{
12+
public PostgresDelayScenario(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
13+
{
14+
}
15+
1116
protected override void ConfigureServices(IServiceCollection services)
1217
{
1318
services.AddWorkflow(cfg =>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using WorkflowCore.IntegrationTests.Scenarios;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
7+
namespace WorkflowCore.Tests.SqlServer.Scenarios
8+
{
9+
[Collection("SqlServer collection")]
10+
public class SqlServerApprovalScenario(ITestOutputHelper testOutputHelper) : ApprovalScenario(testOutputHelper)
11+
{
12+
protected override void ConfigureServices(IServiceCollection services)
13+
{
14+
services.AddWorkflow(x => x.UseSqlServer(SqlDockerSetup.ScenarioConnectionString, true, true));
15+
}
16+
}
17+
}

test/WorkflowCore.Tests.SqlServer/Scenarios/SqlServerDelayScenario.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
using Microsoft.Extensions.DependencyInjection;
33
using WorkflowCore.IntegrationTests.Scenarios;
44
using Xunit;
5+
using Xunit.Abstractions;
56

67
namespace WorkflowCore.Tests.SqlServer.Scenarios
78
{
89
[Collection("SqlServer collection")]
910
public class SqlServerDelayScenario : DelayScenario
10-
{
11+
{
12+
public SqlServerDelayScenario(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
13+
{
14+
}
15+
1116
protected override void ConfigureServices(IServiceCollection services)
1217
{
1318
services.AddWorkflow(cfg =>

0 commit comments

Comments
 (0)