diff --git a/Frank.Testing.EntityFrameworkCore/DbContextBuilder.cs b/Frank.Testing.EntityFrameworkCore/DbContextBuilder.cs
new file mode 100644
index 0000000..8bfc90d
--- /dev/null
+++ b/Frank.Testing.EntityFrameworkCore/DbContextBuilder.cs
@@ -0,0 +1,64 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Frank.Testing.EntityFrameworkCore;
+
+///
+/// Builder class for constructing an instance of DbContext.
+///
+/// The type of DbContext.
+public class DbContextBuilder where T : DbContext
+{
+ private readonly IServiceCollection _serviceCollection = new ServiceCollection();
+
+ private Action? _configuredOptions;
+
+ private ILoggerFactory? _loggerFactory;
+ private string _sqliteConnectionString = "Data Source=:memory:";
+
+ public DbContextBuilder WithLoggerProvider(ILoggerProvider loggerProvider)
+ {
+ _loggerFactory = LoggerFactory.Create(builder => builder.ClearProviders().AddProvider(loggerProvider));
+ return this;
+ }
+
+ public DbContextBuilder WithSqliteConnectionString(string sqliteConnectionString)
+ {
+ _sqliteConnectionString = sqliteConnectionString;
+ return this;
+ }
+
+ public DbContextBuilder WithService(Action configureService)
+ {
+ configureService(_serviceCollection);
+ return this;
+ }
+
+ public DbContextBuilder WithOptions(Action> configureOptions)
+ {
+ _configuredOptions = configureOptions as Action;
+ return this;
+ }
+
+ public T Build()
+ {
+ _serviceCollection.AddDbContext(OptionsAction);
+ var context = _serviceCollection.BuildServiceProvider().GetRequiredService();
+ context.Database.EnsureDeleted();
+ context.Database.EnsureCreated();
+ return context;
+ }
+
+ private void OptionsAction(IServiceProvider arg1, DbContextOptionsBuilder arg2)
+ {
+ _configuredOptions?.Invoke(arg2);
+ if (_loggerFactory != null)
+ arg2.UseLoggerFactory(_loggerFactory);
+
+ arg2.EnableSensitiveDataLogging();
+ arg2.EnableDetailedErrors();
+ arg2.UseSqlite(_sqliteConnectionString);
+ }
+}
\ No newline at end of file
diff --git a/Frank.Testing.EntityFrameworkCore/Frank.Testing.EntityFrameworkCore.csproj b/Frank.Testing.EntityFrameworkCore/Frank.Testing.EntityFrameworkCore.csproj
new file mode 100644
index 0000000..f8ea016
--- /dev/null
+++ b/Frank.Testing.EntityFrameworkCore/Frank.Testing.EntityFrameworkCore.csproj
@@ -0,0 +1,11 @@
+
+
+
+ xUnit.net helpers and extensions for writing automated tests
+ xunit test testing TDD BDD
+
+
+
+
+
+
diff --git a/Frank.Testing.Logging/Frank.Testing.Logging.csproj b/Frank.Testing.Logging/Frank.Testing.Logging.csproj
index 17ca64a..12636ec 100644
--- a/Frank.Testing.Logging/Frank.Testing.Logging.csproj
+++ b/Frank.Testing.Logging/Frank.Testing.Logging.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/Frank.Testing.Logging/LoggingBuilderExtensions.cs b/Frank.Testing.Logging/LoggingBuilderExtensions.cs
index cec129d..60f1b28 100644
--- a/Frank.Testing.Logging/LoggingBuilderExtensions.cs
+++ b/Frank.Testing.Logging/LoggingBuilderExtensions.cs
@@ -27,7 +27,7 @@ public static ILoggingBuilder AddPulseFlowTestLoggingProvider(this ILoggingBuild
options.LogLevel = logLevel;
});
builder.Services.AddPulseFlow(flowBuilder => flowBuilder.AddFlow());
- builder.Services.AddSingleton(provider => new TestLoggerProvider(provider.GetRequiredService()));
+ builder.Services.AddSingleton(provider => new TestLoggerProvider(provider.GetRequiredService(), provider.GetService() ?? new TestLoggerSettings()));
return builder;
}
}
\ No newline at end of file
diff --git a/Frank.Testing.Logging/PulseFlowTestLogger.cs b/Frank.Testing.Logging/PulseFlowTestLogger.cs
index 54c62de..8d9fd1c 100644
--- a/Frank.Testing.Logging/PulseFlowTestLogger.cs
+++ b/Frank.Testing.Logging/PulseFlowTestLogger.cs
@@ -6,14 +6,14 @@
namespace Frank.Testing.Logging;
-public class PulseFlowTestLogger(IConduit conduit, string categoryName) : ILogger
+public class PulseFlowTestLogger(IConduit conduit, string categoryName, TestLoggerSettings settings) : ILogger
{
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
=> conduit.SendAsync(new LogPulse(logLevel, eventId, exception, categoryName, formatter(state, exception))).GetAwaiter().GetResult();
- public bool IsEnabled(LogLevel logLevel) => true;
+ public bool IsEnabled(LogLevel logLevel) => logLevel >= settings.LogLevel;
public IDisposable? BeginScope(TState state) where TState : notnull => new PulseFlowLoggerScope(state);
}
-public class PulseFlowTestLogger(IConduit conduit) : PulseFlowTestLogger(conduit, typeof(T).GetDisplayName()), ILogger;
\ No newline at end of file
+public class PulseFlowTestLogger(IConduit conduit, TestLoggerSettings settings) : PulseFlowTestLogger(conduit, typeof(T).GetDisplayName(), settings), ILogger;
\ No newline at end of file
diff --git a/Frank.Testing.Logging/TestLoggerProvider.cs b/Frank.Testing.Logging/TestLoggerProvider.cs
index 013a8e1..97ffa89 100644
--- a/Frank.Testing.Logging/TestLoggerProvider.cs
+++ b/Frank.Testing.Logging/TestLoggerProvider.cs
@@ -6,7 +6,7 @@
namespace Frank.Testing.Logging;
-public class TestLoggerProvider(IConduit conduit) : ILoggerProvider
+public class TestLoggerProvider(IConduit conduit, TestLoggerSettings settings) : ILoggerProvider
{
private readonly ConcurrentDictionary _loggers = new();
@@ -15,7 +15,7 @@ public ILogger CreateLogger(string categoryName)
if (_loggers.TryGetValue(categoryName, out var logger))
return logger;
- var newLogger = new PulseFlowTestLogger(conduit, categoryName);
+ var newLogger = new PulseFlowTestLogger(conduit, categoryName, settings);
return _loggers.GetOrAdd(categoryName, newLogger);
}
diff --git a/Frank.Testing.Logging/TestLoggingOutputFlow.cs b/Frank.Testing.Logging/TestLoggingOutputFlow.cs
index cd3a1a9..9c40151 100644
--- a/Frank.Testing.Logging/TestLoggingOutputFlow.cs
+++ b/Frank.Testing.Logging/TestLoggingOutputFlow.cs
@@ -14,7 +14,7 @@ public async Task HandleAsync(IPulse pulse, CancellationToken cancellationToken)
{
if (pulse is LogPulse logPulse)
{
- outputHelper.WriteLine(logPulse.Message);
+ outputHelper.WriteLine(logPulse.ToString());
await Task.CompletedTask;
}
}
diff --git a/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj b/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj
index a438db1..4c1ade4 100644
--- a/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj
+++ b/Frank.Testing.TestOutputExtensions/Frank.Testing.TestOutputExtensions.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/Frank.Testing.Tests/EntityFramworkCoreTests/DbContextBuilderTests.cs b/Frank.Testing.Tests/EntityFramworkCoreTests/DbContextBuilderTests.cs
new file mode 100644
index 0000000..b48f233
--- /dev/null
+++ b/Frank.Testing.Tests/EntityFramworkCoreTests/DbContextBuilderTests.cs
@@ -0,0 +1,111 @@
+using Frank.PulseFlow;
+using Frank.PulseFlow.Logging;
+using Frank.Testing.EntityFrameworkCore;
+using Frank.Testing.Logging;
+using Frank.Testing.Tests.TestingInfrastructure;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
+
+using Xunit.Abstractions;
+
+namespace Frank.Testing.Tests.EntityFramworkCoreTests;
+
+public class DbContextBuilderTests(ITestOutputHelper outputHelper)
+{
+ [Fact]
+ public void Build_WithLoggerProvider_UsesLoggerProvider()
+ {
+ var conduit = new TestConduit();
+ var loggerProvider = new TestLoggerProvider(conduit, new TestLoggerSettings());
+ var dbContext = new DbContextBuilder()
+ .WithLoggerProvider(loggerProvider)
+ .Build();
+ dbContext.Database.EnsureCreated();
+ dbContext.Database.ExecuteSqlRaw("SELECT 1");
+ dbContext.Dispose();
+
+ outputHelper.WriteJson(conduit.Logs);
+ // var log = conduit.Logs.Single();
+ // Assert.Equal("SELECT 1", log.Message);
+ }
+
+ [Fact]
+ public void Build_WithService_UsesService()
+ {
+ var dbContext = new DbContextBuilder()
+ .WithService(services => services.AddSingleton())
+ .Build();
+ Assert.NotNull(dbContext.GetService());
+ }
+
+ [Fact]
+ public void Build_WithOptions_UsesOptions()
+ {
+ var dbContext = new DbContextBuilder()
+ .WithOptions(options => options.UseSqlite("Data Source=:memory:"))
+ .Build();
+ dbContext.Database.EnsureCreated();
+ dbContext.Database.ExecuteSqlRaw("SELECT 1");
+ dbContext.Dispose();
+ }
+
+ [Fact]
+ public void Build_WithLoggerProviderAndService_UsesLoggerProviderAndService()
+ {
+ var conduit = new TestConduit();
+ var loggerProvider = new TestLoggerProvider(conduit, new TestLoggerSettings());
+ var dbContext = new DbContextBuilder()
+ .WithLoggerProvider(loggerProvider)
+ .WithSqliteConnectionString("Data Source=MyTestDatabase.db")
+ .WithService(services => services.AddSingleton())
+ .Build();
+ dbContext.Database.EnsureCreated();
+ dbContext.Persons.Add(new TestPerson() { Name = "Frank" });
+ dbContext.SaveChanges();
+ dbContext.Dispose();
+ }
+
+ public class TestService : ITestService
+ {
+ public async Task DoSomethingAsync()
+ {
+ await Task.CompletedTask;
+ }
+ }
+
+ public interface ITestService
+ {
+ Task DoSomethingAsync();
+ }
+
+ [Fact]
+ public void Build_WithLoggerProviderAndOptions_UsesLoggerProviderAndOptions()
+ {
+ var conduit = new TestConduit();
+ var loggerProvider = new TestLoggerProvider(conduit, new TestLoggerSettings());
+ var dbContext = new DbContextBuilder()
+ .WithSqliteConnectionString("Data Source=MyTestDatabase.db")
+ .WithLoggerProvider(loggerProvider)
+ .Build();
+ dbContext.Database.EnsureCreated();
+ dbContext.Persons.Add(new TestPerson() { Name = "Frank" });
+ dbContext.SaveChanges();
+ dbContext.Database.ExecuteSqlRaw("SELECT 1");
+ dbContext.Dispose();
+
+ outputHelper.WriteJson(conduit.Logs);
+ }
+
+ public class TestConduit : IConduit
+ {
+ public async Task SendAsync(IPulse message)
+ {
+ await Task.CompletedTask;
+ Logs.Add(message as LogPulse ?? throw new InvalidOperationException());
+ }
+
+ public List Logs { get; } = new();
+ }
+}
diff --git a/Frank.Testing.Tests/Frank.Testing.Tests.csproj b/Frank.Testing.Tests/Frank.Testing.Tests.csproj
index d779898..75f4672 100644
--- a/Frank.Testing.Tests/Frank.Testing.Tests.csproj
+++ b/Frank.Testing.Tests/Frank.Testing.Tests.csproj
@@ -14,7 +14,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -27,6 +27,7 @@
+
diff --git a/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs b/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs
index c17d3f2..63a668b 100644
--- a/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs
+++ b/Frank.Testing.Tests/TestLogging/TestLoggingTests.cs
@@ -13,7 +13,7 @@ public class TestLoggingTests(ITestOutputHelper outputHelper)
[Fact]
public async Task Test1()
{
- var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
var host = CreateHostBuilder().Build();
await host.RunAsync(cancellationTokenSource.Token);
@@ -40,7 +40,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Hello from MyService");
- await Task.Delay(1000, stoppingToken);
+ await Task.Delay(100, stoppingToken);
}
}
}
diff --git a/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs b/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs
index fef89b5..f3b8007 100644
--- a/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs
+++ b/Frank.Testing.Tests/TestingInfrastructure/TestAddress.cs
@@ -2,6 +2,8 @@
public class TestAddress
{
+ public Guid Id { get; set; }
+
public string City { get; set; }
public int ZipCode { get; set; }
diff --git a/Frank.Testing.Tests/TestingInfrastructure/TestDbContext.cs b/Frank.Testing.Tests/TestingInfrastructure/TestDbContext.cs
new file mode 100644
index 0000000..8d8b9b5
--- /dev/null
+++ b/Frank.Testing.Tests/TestingInfrastructure/TestDbContext.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace Frank.Testing.Tests.TestingInfrastructure;
+
+public class TestDbContext : DbContext
+{
+ public TestDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet Persons { get; set; }
+
+ public DbSet Addresses { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().HasKey(e => e.Id);
+ modelBuilder.Entity().HasKey(e => e.Id);
+ base.OnModelCreating(modelBuilder);
+ }
+}
\ No newline at end of file
diff --git a/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs b/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs
index d16f6f5..c6bb8e8 100644
--- a/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs
+++ b/Frank.Testing.Tests/TestingInfrastructure/TestPerson.cs
@@ -2,6 +2,7 @@
public class TestPerson
{
+ public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
diff --git a/Frank.Testing.sln b/Frank.Testing.sln
index 0e5aa9c..b0ace23 100644
--- a/Frank.Testing.sln
+++ b/Frank.Testing.sln
@@ -22,6 +22,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.TestOutputExt
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.ApiTesting", "Frank.Testing.ApiTesting\Frank.Testing.ApiTesting.csproj", "{3B30D75C-5B95-4E87-B862-7E8DC49038DF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frank.Testing.EntityFrameworkCore", "Frank.Testing.EntityFrameworkCore\Frank.Testing.EntityFrameworkCore.csproj", "{A64BD8FC-364F-40E5-A276-F1DEA9D98F67}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -47,5 +49,9 @@ Global
{3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B30D75C-5B95-4E87-B862-7E8DC49038DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A64BD8FC-364F-40E5-A276-F1DEA9D98F67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A64BD8FC-364F-40E5-A276-F1DEA9D98F67}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A64BD8FC-364F-40E5-A276-F1DEA9D98F67}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A64BD8FC-364F-40E5-A276-F1DEA9D98F67}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal