Skip to content

Commit 17d3640

Browse files
authored
Merge pull request #99 from koenbeuk/feature/functionaltests
Functional test template
2 parents 26bf2cd + 4ba5e24 commit 17d3640

File tree

7 files changed

+207
-9
lines changed

7 files changed

+207
-9
lines changed

EntityFrameworkCore.Triggered.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Trigger
6161
EndProject
6262
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Triggered.Extensions.Tests", "test\EntityFrameworkCore.Triggered.Extensions.Tests\EntityFrameworkCore.Triggered.Extensions.Tests.csproj", "{57F94F2C-81C4-4304-8F5A-FD4F17A2698B}"
6363
EndProject
64+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkCore.Triggered.IntegrationTests", "test\EntityFrameworkCore.Triggered.IntegrationTests\EntityFrameworkCore.Triggered.IntegrationTests.csproj", "{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}"
65+
EndProject
6466
Global
6567
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6668
Debug|Any CPU = Debug|Any CPU
@@ -197,6 +199,14 @@ Global
197199
{57F94F2C-81C4-4304-8F5A-FD4F17A2698B}.Release|Any CPU.Build.0 = Release|Any CPU
198200
{57F94F2C-81C4-4304-8F5A-FD4F17A2698B}.ReleaseV1|Any CPU.ActiveCfg = ReleaseV1|Any CPU
199201
{57F94F2C-81C4-4304-8F5A-FD4F17A2698B}.ReleaseV1|Any CPU.Build.0 = ReleaseV1|Any CPU
202+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
203+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.Debug|Any CPU.Build.0 = Debug|Any CPU
204+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.DebugV1|Any CPU.ActiveCfg = DebugV1|Any CPU
205+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.DebugV1|Any CPU.Build.0 = DebugV1|Any CPU
206+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.Release|Any CPU.ActiveCfg = Release|Any CPU
207+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.Release|Any CPU.Build.0 = Release|Any CPU
208+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.ReleaseV1|Any CPU.ActiveCfg = ReleaseV1|Any CPU
209+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F}.ReleaseV1|Any CPU.Build.0 = ReleaseV1|Any CPU
200210
EndGlobalSection
201211
GlobalSection(SolutionProperties) = preSolution
202212
HideSolutionNode = FALSE
@@ -220,6 +230,7 @@ Global
220230
{513E2D7F-628C-49DB-A7ED-15A9CCF27E6A} = {E24FAD52-9971-489B-AED1-1D977E545DD4}
221231
{27A4A75B-112E-4B3E-867A-5F0D27F74CF8} = {EDFABD48-3C79-47AE-B84C-47CE2A52C20D}
222232
{57F94F2C-81C4-4304-8F5A-FD4F17A2698B} = {0FAE4F6A-93BB-453C-8FB4-B24A9F30DA59}
233+
{EE979DC2-F993-412E-9CA1-5EE8FB9E249F} = {0FAE4F6A-93BB-453C-8FB4-B24A9F30DA59}
223234
EndGlobalSection
224235
GlobalSection(ExtensibilityGlobals) = postSolution
225236
SolutionGuid = {847A0017-23D6-4513-B78E-CAADBD836A7D}

src/EntityFrameworkCore.Triggered/Infrastructure/Internal/TriggersOptionExtension.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,7 @@ public void ApplyServices(IServiceCollection services)
180180

181181
var instanceParamExpression = Expression.Parameter(typeof(object), "object");
182182

183-
var triggerInstanceFactoryBuilder =
184-
Expression.Lambda<Func<object?, object>>(
185-
Expression.New(
186-
typeof(TriggerInstanceFactory<>).MakeGenericType(triggerServiceType).GetConstructor(new[] { typeof(object) }),
187-
instanceParamExpression
188-
),
189-
instanceParamExpression
190-
)
191-
.Compile();
183+
Func<object?, object>? triggerInstanceFactoryBuilder = null;
192184

193185
foreach (var triggerType in _triggerTypes.Distinct())
194186
{
@@ -198,6 +190,19 @@ public void ApplyServices(IServiceCollection services)
198190

199191
foreach (var triggerTypeImplementation in triggerTypeImplementations)
200192
{
193+
if (triggerInstanceFactoryBuilder is null)
194+
{
195+
triggerInstanceFactoryBuilder =
196+
Expression.Lambda<Func<object?, object>>(
197+
Expression.New(
198+
typeof(TriggerInstanceFactory<>).MakeGenericType(triggerServiceType).GetConstructor(new[] { typeof(object) }),
199+
instanceParamExpression
200+
),
201+
instanceParamExpression
202+
)
203+
.Compile();
204+
}
205+
201206
var triggerTypeImplementationFactoryType = typeof(ITriggerInstanceFactory<>).MakeGenericType(triggerTypeImplementation);
202207
services.Add(new ServiceDescriptor(triggerTypeImplementationFactoryType, _ => triggerInstanceFactoryBuilder(triggerServiceInstance), lifetime));
203208
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using EntityFrameworkCore.Triggered.IntegrationTests.Models;
7+
using Microsoft.EntityFrameworkCore;
8+
9+
namespace EntityFrameworkCore.Triggered.IntegrationTests
10+
{
11+
public class ApplicationDbContext
12+
#if EFCORETRIGGERED1
13+
: TriggeredDbContext
14+
#else
15+
: DbContext
16+
#endif
17+
{
18+
readonly string _databaseName;
19+
20+
public ApplicationDbContext(string databaseName)
21+
{
22+
_databaseName = databaseName;
23+
}
24+
25+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
26+
{
27+
optionsBuilder.UseInMemoryDatabase(_databaseName);
28+
optionsBuilder.UseTriggers(triggerOptions => {
29+
triggerOptions.AddAssemblyTriggers();
30+
});
31+
}
32+
33+
public DbSet<User> Users { get; set; }
34+
35+
protected override void OnModelCreating(ModelBuilder modelBuilder)
36+
{
37+
}
38+
}
39+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
<Nullable>disable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.5" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
12+
<PackageReference Include="xunit" Version="2.4.1" />
13+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
14+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
15+
<PrivateAssets>all</PrivateAssets>
16+
</PackageReference>
17+
<PackageReference Include="coverlet.collector" Version="1.3.0">
18+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19+
<PrivateAssets>all</PrivateAssets>
20+
</PackageReference>
21+
<PackageReference Include="ScenarioTests.XUnit" Version="0.3.0" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\..\src\EntityFrameworkCore.Triggered.Extensions\EntityFrameworkCore.Triggered.Extensions.csproj" />
26+
</ItemGroup>
27+
28+
</Project>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace EntityFrameworkCore.Triggered.IntegrationTests.Models
8+
{
9+
public class User
10+
{
11+
public int Id { get; set; }
12+
13+
public string UserName { get; set; }
14+
15+
public DateTime? DeletedDate { get; set; }
16+
}
17+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using EntityFrameworkCore.Triggered.IntegrationTests.Models;
7+
using ScenarioTests;
8+
using Microsoft.EntityFrameworkCore;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Xunit;
11+
12+
namespace EntityFrameworkCore.Triggered.IntegrationTests
13+
{
14+
public partial class TestScenario
15+
{
16+
[Scenario(NamingPolicy = ScenarioTestMethodNamingPolicy.Test)]
17+
public void TestScenario1(ScenarioContext scenario)
18+
{
19+
const int sampleUsersCount = 100;
20+
21+
var dbContext = new ApplicationDbContext(scenario.TargetName);
22+
23+
// step 1: Populate database with users
24+
dbContext.Users.AddRange(Enumerable.Range(0, sampleUsersCount).Select(x => new User {
25+
UserName = $"user{x}"
26+
}));
27+
28+
dbContext.SaveChanges();
29+
30+
scenario.Fact("Database saved all users", () => {
31+
var usersCount = dbContext.Users.Count();
32+
Assert.Equal(sampleUsersCount, usersCount);
33+
});
34+
35+
// step 2: Delete the last 50 users
36+
dbContext.RemoveRange(
37+
dbContext.Users.Where(x => x.Id > sampleUsersCount / 2)
38+
);
39+
40+
dbContext.SaveChanges();
41+
42+
scenario.Fact("We should still have all our users", () => {
43+
var usersCount = dbContext.Users.Count();
44+
Assert.Equal(sampleUsersCount, usersCount);
45+
});
46+
47+
scenario.Fact("However, half of them will have been soft deleted", () => {
48+
var usersCount = dbContext.Users.Where(x => x.DeletedDate != null).Count();
49+
Assert.Equal(sampleUsersCount / 2, usersCount);
50+
});
51+
52+
// step 3: Undo deletion of our users
53+
{
54+
var users = dbContext.Users.Where(x => x.DeletedDate != null);
55+
foreach (var user in users)
56+
{
57+
user.DeletedDate = null;
58+
}
59+
60+
dbContext.SaveChanges();
61+
}
62+
63+
scenario.Fact("Ensure that all users are restored", () => {
64+
var usersCount = dbContext.Users.Where(x => x.DeletedDate != null).Count();
65+
Assert.Equal(0, usersCount);
66+
});
67+
}
68+
}
69+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using EntityFrameworkCore.Triggered.Extensions;
7+
using EntityFrameworkCore.Triggered.IntegrationTests.Models;
8+
9+
namespace EntityFrameworkCore.Triggered.IntegrationTests.Triggers.Users
10+
{
11+
public class SoftDeleteUsers : Trigger<User>
12+
{
13+
readonly ApplicationDbContext _applicationDbContext;
14+
15+
public SoftDeleteUsers(ApplicationDbContext applicationDbContext)
16+
{
17+
_applicationDbContext = applicationDbContext;
18+
}
19+
20+
public override void BeforeSave(ITriggerContext<User> context)
21+
{
22+
if (context.ChangeType is ChangeType.Deleted)
23+
{
24+
_applicationDbContext.Entry(context.Entity).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
25+
context.Entity.DeletedDate = DateTime.UtcNow;
26+
}
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)