Skip to content

Improve speed of running tests #1218

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

Merged
merged 5 commits into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 0 additions & 6 deletions test/AnnotationTests/AnnotationTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.0' ">
<Using Remove="System.Net.Http" />
</ItemGroup>
Expand Down
6 changes: 0 additions & 6 deletions test/DiscoveryTests/DiscoveryTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
<TargetFramework>$(TargetFrameworkName)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Examples\JsonApiDotNetCoreExample\JsonApiDotNetCoreExample.csproj" />
<ProjectReference Include="..\TestBuildingBlocks\TestBuildingBlocks.csproj" />
Expand Down
4 changes: 0 additions & 4 deletions test/DiscoveryTests/xunit.runner.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<MusicTrack>()
.HasMany(musicTrack => musicTrack.OccursIn)
.WithMany(playlist => playlist.Tracks);

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.Transactions;

public sealed class AtomicTransactionConsistencyTests : IClassFixture<IntegrationTestContext<TestableStartup<OperationsDbContext>, OperationsDbContext>>
public sealed class AtomicTransactionConsistencyTests
: IClassFixture<IntegrationTestContext<TestableStartup<OperationsDbContext>, OperationsDbContext>>, IAsyncLifetime
{
private readonly IntegrationTestContext<TestableStartup<OperationsDbContext>, OperationsDbContext> _testContext;
private readonly OperationsFakers _fakers = new();
Expand All @@ -27,7 +28,7 @@ public AtomicTransactionConsistencyTests(IntegrationTestContext<TestableStartup<
services.AddResourceRepository<LyricRepository>();

string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres";
string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}";
string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-Extra-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}";

services.AddDbContext<ExtraDbContext>(options => options.UseNpgsql(dbConnectionString));
});
Expand Down Expand Up @@ -158,4 +159,22 @@ public async Task Cannot_use_distributed_transaction()
error.Source.ShouldNotBeNull();
error.Source.Pointer.Should().Be("/atomic:operations[0]");
}

public Task InitializeAsync()
{
return Task.CompletedTask;
}

Task IAsyncLifetime.DisposeAsync()
{
return DeleteExtraDatabaseAsync();
}

private async Task DeleteExtraDatabaseAsync()
{
await using AsyncServiceScope scope = _testContext.Factory.Services.CreateAsyncScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ExtraDbContext>();

await dbContext.Database.EnsureDeletedAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<Car>()
.HasMany(car => car.PreviousDealerships)
.WithMany(dealership => dealership.SoldCars);

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ protected override void OnModelCreating(ModelBuilder builder)
.HasOne(building => building.SecondaryDoor)
.WithOne()
.HasForeignKey<Building>("SecondaryDoorId");

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<SystemDirectory>()
.HasOne(systemDirectory => systemDirectory.AlsoSelf)
.WithOne();

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder)
.HasOne(photo => photo.Location)
.WithOne(location => location.Photo)
.HasForeignKey<Photo>("LocationId");

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder)

builder.Entity<WebProduct>()
.HasQueryFilter(webProduct => webProduct.Shop.TenantId == _tenantProvider.TenantId);

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<FilterableResource>()
.Property(resource => resource.SomeDateTimeInLocalZone)
.HasColumnType("timestamp without time zone");

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder)
.HasOne(man => man.Wife)
.WithOne(woman => woman.Husband)
.HasForeignKey<Man>();

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ protected override void OnModelCreating(ModelBuilder builder)
left => left
.HasOne(joinEntity => joinEntity.ToItem)
.WithMany());

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ protected override void OnModelCreating(ModelBuilder builder)
.HasOne(order => order.Shipment)
.WithOne(shipment => shipment.Order)
.HasForeignKey<Shipment>("OrderId");

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<Scholarship>()
.HasMany(scholarship => scholarship.Participants)
.WithOne(student => student.Scholarship!);

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2429,7 +2429,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_e

await _testContext.RunOnDatabaseAsync(async dbContext =>
{
await dbContext.ClearTableAsync<Vehicle>();
await dbContext.ClearTableAsync<Wheel>();
dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2);
await dbContext.SaveChangesAsync();
});
Expand Down Expand Up @@ -2487,7 +2487,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_l

await _testContext.RunOnDatabaseAsync(async dbContext =>
{
await dbContext.ClearTableAsync<Vehicle>();
await dbContext.ClearTableAsync<Wheel>();
dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2);
await dbContext.SaveChangesAsync();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<GenericProperty>().ToTable("GenericProperties");
builder.Entity<StringProperty>().ToTable("StringProperties");
builder.Entity<NumberProperty>().ToTable("NumberProperties");

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder)

builder.Entity<Department>()
.HasQueryFilter(department => department.SoftDeletedAt == null);

base.OnModelCreating(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<Player>()
.HasOne(player => player.ActiveGame)
.WithMany(game => game.ActivePlayers);

base.OnModelCreating(builder);
}
}
6 changes: 0 additions & 6 deletions test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
<TargetFramework>$(TargetFrameworkName)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TestBuildingBlocks\TestBuildingBlocks.csproj" />
<ProjectReference Include="..\..\src\JsonApiDotNetCore.SourceGenerators\JsonApiDotNetCore.SourceGenerators.csproj" OutputItemType="Analyzer"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private static IHostBuilder CreateValidatingHostBuilder()
IHostBuilder hostBuilder = Host.CreateDefaultBuilder().ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureServices(services =>
services.AddDbContext<DependencyContainerRegistrationDbContext>(options => options.UseInMemoryDatabase("db")));
services.AddDbContext<DependencyContainerRegistrationDbContext>(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString())));

webBuilder.UseStartup<TestableStartup<DependencyContainerRegistrationDbContext>>();

Expand Down
5 changes: 0 additions & 5 deletions test/JsonApiDotNetCoreTests/xunit.runner.json

This file was deleted.

6 changes: 0 additions & 6 deletions test/MultiDbContextTests/MultiDbContextTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
<TargetFramework>$(TargetFrameworkName)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Examples\MultiDbContextExample\MultiDbContextExample.csproj" />
<ProjectReference Include="..\TestBuildingBlocks\TestBuildingBlocks.csproj" />
Expand Down
5 changes: 0 additions & 5 deletions test/MultiDbContextTests/xunit.runner.json

This file was deleted.

13 changes: 1 addition & 12 deletions test/TestBuildingBlocks/DbContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Npgsql;

namespace TestBuildingBlocks;

Expand Down Expand Up @@ -36,17 +35,7 @@ private static async Task ClearTablesAsync(this DbContext dbContext, params Type
}

string tableName = entityType.GetTableName()!;

// PERF: We first try to clear the table, which is fast and usually succeeds, unless foreign key constraints are violated.
// In that case, we recursively delete all related data, which is slow.
try
{
await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\"");
}
catch (PostgresException)
{
await dbContext.Database.ExecuteSqlRawAsync($"truncate table \"{tableName}\" cascade");
}
await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\"");
}
}
}
19 changes: 17 additions & 2 deletions test/TestBuildingBlocks/IntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
using System.Text;
using System.Text.Json;
using JsonApiDotNetCore.Middleware;
using Xunit;

namespace TestBuildingBlocks;

/// <summary>
/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints.
/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints. It throttles tests that are running in parallel
/// to avoid exceeding the maximum active database connections.
/// </summary>
public abstract class IntegrationTest
public abstract class IntegrationTest : IAsyncLifetime
{
private static readonly SemaphoreSlim ThrottleSemaphore = new(64);

protected abstract JsonSerializerOptions SerializerOptions { get; }

public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteHeadAsync<TResponseDocument>(string requestUrl,
Expand Down Expand Up @@ -105,4 +109,15 @@ public abstract class IntegrationTest
throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception);
}
}

public async Task InitializeAsync()
{
await ThrottleSemaphore.WaitAsync();
}

public virtual Task DisposeAsync()
{
_ = ThrottleSemaphore.Release();
return Task.CompletedTask;
}
}
28 changes: 17 additions & 11 deletions test/TestBuildingBlocks/IntegrationTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace TestBuildingBlocks;
/// The Entity Framework Core database context, which can be defined in the test project or API project.
/// </typeparam>
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public class IntegrationTestContext<TStartup, TDbContext> : IntegrationTest, IDisposable
public class IntegrationTestContext<TStartup, TDbContext> : IntegrationTest
where TStartup : class
where TDbContext : TestableDbContext
{
Expand Down Expand Up @@ -103,16 +103,6 @@ private WebApplicationFactory<TStartup> CreateFactory()
return factoryWithConfiguredContentRoot;
}

public void Dispose()
{
if (_lazyFactory.IsValueCreated)
{
RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync()).Wait();

_lazyFactory.Value.Dispose();
}
}

public void ConfigureLogging(Action<ILoggingBuilder> loggingConfiguration)
{
_loggingConfiguration = loggingConfiguration;
Expand All @@ -136,6 +126,22 @@ public async Task RunOnDatabaseAsync(Func<TDbContext, Task> asyncAction)
await asyncAction(dbContext);
}

public override async Task DisposeAsync()
{
try
{
if (_lazyFactory.IsValueCreated)
{
await RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync());
await _lazyFactory.Value.DisposeAsync();
}
}
finally
{
await base.DisposeAsync();
}
}

private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory<TStartup>
{
private Action<ILoggingBuilder>? _loggingConfiguration;
Expand Down
12 changes: 12 additions & 0 deletions test/TestBuildingBlocks/TestableDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics;
using JsonApiDotNetCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;

namespace TestBuildingBlocks;
Expand All @@ -17,4 +18,15 @@ protected override void OnConfiguring(DbContextOptionsBuilder builder)
// Writes SQL statements to the Output Window when debugging.
builder.LogTo(message => Debug.WriteLine(message), DbLoggerCategory.Database.Name.AsArray(), LogLevel.Information);
}

protected override void OnModelCreating(ModelBuilder builder)
{
foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes().SelectMany(entityType => entityType.GetForeignKeys()))
{
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull)
{
foreignKey.DeleteBehavior = DeleteBehavior.SetNull;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void RegisterResource_DeviatingDbContextPropertyName_RegistersCorrectly()
// Arrange
var services = new ServiceCollection();
services.AddLogging();
services.AddDbContext<TestDbContext>(options => options.UseInMemoryDatabase("UnitTestDb"));
services.AddDbContext<TestDbContext>(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()));

// Act
services.AddJsonApi<TestDbContext>();
Expand Down
Loading