Skip to content

Commit cbc4e5c

Browse files
adrianhallahall
andauthored
build: migrate test suite to xUnit v3 and Xunit.Combinatorial v2 (#467) (#472)
- Replace xunit 2.9.3 with xunit.v3 3.2.2; test projects become stand-alone executables (OutputType=Exe) gated on IsTestProject. - Bump Xunit.Combinatorial 1.6.24 -> 2.0.24 (v2 targets xUnit v3). - Remove XUnit.SkippableFact (no v3 release) and unused xRetry. - Rewrite [SkippableFact]/[SkippableTheory] -> [Fact]/[Theory] and Skip.IfNot(...) -> Assert.SkipUnless(..., reason) using native v3 dynamic skip. - Remove Xunit.Abstractions usings (ITestOutputHelper now in Xunit). - Convert IAsyncLifetime InitializeAsync/DisposeAsync to ValueTask; fold IDisposable cleanup into DisposeAsync where both were implemented. - Keep VSTest path (Microsoft.NET.Test.Sdk + xunit.runner.visualstudio + coverlet) so dotnet test and XPlat Code Coverage are unchanged. No test logic changed; no tests disabled or deleted. Co-authored-by: ahall <ahall@cloudflare.com>
1 parent a02ea92 commit cbc4e5c

34 files changed

Lines changed: 764 additions & 784 deletions

Directory.Packages.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@
4747
<PackageVersion Include="TestContainers.MongoDb" Version="$(TestContainersVersion)" />
4848
<PackageVersion Include="TestContainers.MsSql" Version="$(TestContainersVersion)" />
4949
<PackageVersion Include="TestContainers.PostgreSql" Version="$(TestContainersVersion)" />
50-
<PackageVersion Include="xRetry" Version="1.9.0" />
51-
<PackageVersion Include="xunit" Version="2.9.3" />
50+
<PackageVersion Include="xunit.v3" Version="3.2.2" />
51+
<PackageVersion Include="xunit.v3.core" Version="3.2.2" />
52+
<PackageVersion Include="xunit.v3.assert" Version="3.2.2" />
5253
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
5354
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
5455
<PackageVersion Include="coverlet.msbuild" Version="10.0.1" />
5556
<PackageVersion Include="Ulid" Version="1.4.1" />
56-
<!-- Do not change XUnit.Combinatorial to v2 (xUnit v2 compatibility) -->
57-
<PackageVersion Include="XUnit.Combinatorial" Version="1.6.24" />
58-
<PackageVersion Include="XUnit.SkippableFact" Version="1.5.61" />
57+
<!-- XUnit.Combinatorial v2.x targets xUnit v3 (xunit.v3.extensibility.core) -->
58+
<PackageVersion Include="XUnit.Combinatorial" Version="2.0.24" />
5959
</ItemGroup>
6060
</Project>

tests/CommunityToolkit.Datasync.Server.Automapper.Test/MappedTableRepository_Tests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using CommunityToolkit.Datasync.TestCommon.Databases;
1010
using Microsoft.EntityFrameworkCore;
1111
using Microsoft.Extensions.Logging;
12-
using Xunit.Abstractions;
1312

1413
namespace CommunityToolkit.Datasync.Server.Automapper.Test;
1514

tests/CommunityToolkit.Datasync.Server.CosmosDb.Test/CosmosDbRepository_Tests.cs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace CommunityToolkit.Datasync.Server.CosmosDb.Test;
1616

1717
[ExcludeFromCodeCoverage]
18-
public class CosmosDbRepository_Tests : RepositoryTests<CosmosDbMovie>, IDisposable, IAsyncLifetime
18+
public class CosmosDbRepository_Tests : RepositoryTests<CosmosDbMovie>, IAsyncLifetime
1919
{
2020
#region Setup
2121
private readonly Random random = new();
@@ -26,20 +26,6 @@ public class CosmosDbRepository_Tests : RepositoryTests<CosmosDbMovie>, IDisposa
2626
private Container _container;
2727
private CosmosTableRepository<CosmosDbMovie> _repository;
2828

29-
public void Dispose()
30-
{
31-
Dispose(true);
32-
GC.SuppressFinalize(this);
33-
}
34-
35-
protected virtual void Dispose(bool disposing)
36-
{
37-
if (disposing)
38-
{
39-
this._client?.Dispose();
40-
}
41-
}
42-
4329
override protected bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);
4430

4531
protected override async Task<CosmosDbMovie> GetEntityAsync(string id)
@@ -69,7 +55,7 @@ protected override Task<string> GetRandomEntityIdAsync(bool exists)
6955
return Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
7056
}
7157

72-
public async Task InitializeAsync()
58+
public async ValueTask InitializeAsync()
7359
{
7460
if (!string.IsNullOrEmpty(this.connectionString))
7561
{
@@ -137,7 +123,7 @@ public async Task InitializeAsync()
137123
}
138124
}
139125

140-
public async Task DisposeAsync()
126+
public async ValueTask DisposeAsync()
141127
{
142128
if (this._client != null)
143129
{
@@ -149,6 +135,8 @@ public async Task DisposeAsync()
149135
{
150136
// Ignore
151137
}
138+
139+
this._client.Dispose();
152140
}
153141
}
154142
#endregion

tests/CommunityToolkit.Datasync.Server.CosmosDb.Test/PackedKeyRepository_Tests.cs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
namespace CommunityToolkit.Datasync.Server.CosmosDb.Test;
1919

2020
[ExcludeFromCodeCoverage]
21-
public class PackedKeyRepository_Tests : RepositoryTests<CosmosDbMovie>, IDisposable, IAsyncLifetime
21+
public class PackedKeyRepository_Tests : RepositoryTests<CosmosDbMovie>, IAsyncLifetime
2222
{
2323
#region Setup
2424
private readonly Random random = new();
@@ -29,20 +29,6 @@ public class PackedKeyRepository_Tests : RepositoryTests<CosmosDbMovie>, IDispos
2929
private Container _container;
3030
private CosmosTableRepository<CosmosDbMovie> _repository;
3131

32-
public void Dispose()
33-
{
34-
Dispose(true);
35-
GC.SuppressFinalize(this);
36-
}
37-
38-
protected virtual void Dispose(bool disposing)
39-
{
40-
if (disposing)
41-
{
42-
this._client?.Dispose();
43-
}
44-
}
45-
4632
override protected bool CanRunLiveTests() => !string.IsNullOrEmpty(this.connectionString);
4733
protected override async Task<CosmosDbMovie> GetEntityAsync(string id)
4834
{
@@ -83,7 +69,7 @@ protected override Task<string> GetRandomEntityIdAsync(bool exists)
8369
return Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : $"{Guid.NewGuid()}:2018");
8470
}
8571

86-
public async Task InitializeAsync()
72+
public async ValueTask InitializeAsync()
8773
{
8874
if (!string.IsNullOrEmpty(this.connectionString))
8975
{
@@ -152,7 +138,7 @@ public async Task InitializeAsync()
152138
}
153139
}
154140

155-
public async Task DisposeAsync()
141+
public async ValueTask DisposeAsync()
156142
{
157143
if (this._client != null)
158144
{
@@ -164,29 +150,31 @@ public async Task DisposeAsync()
164150
{
165151
// Ignore
166152
}
153+
154+
this._client.Dispose();
167155
}
168156
}
169157
#endregion
170158

171-
[SkippableTheory]
159+
[Theory]
172160
[InlineData("BadId")]
173161
[InlineData("12345-12345")]
174162
public async Task ReadAsync_Throws_OnMalformedId(string id)
175163
{
176-
Skip.IfNot(CanRunLiveTests());
164+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
177165

178166
IRepository<CosmosDbMovie> Repository = await GetPopulatedRepositoryAsync();
179167
Func<Task> act = async () => _ = await Repository.ReadAsync(id);
180168

181169
(await act.Should().ThrowAsync<HttpException>()).WithStatusCode(400);
182170
}
183171

184-
[SkippableTheory]
172+
[Theory]
185173
[InlineData("BadId")]
186174
[InlineData("12345-12345")]
187175
public async Task DeleteAsync_Throws_OnMalformedIds(string id)
188176
{
189-
Skip.IfNot(CanRunLiveTests());
177+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
190178

191179
IRepository<CosmosDbMovie> Repository = await GetPopulatedRepositoryAsync();
192180
Func<Task> act = async () => await Repository.DeleteAsync(id);

tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/AzureSqlEntityTableRepository_Tests.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using CommunityToolkit.Datasync.TestCommon.Databases;
77
using CommunityToolkit.Datasync.TestCommon.Fixtures;
88
using Microsoft.EntityFrameworkCore;
9-
using Xunit.Abstractions;
109

1110
#pragma warning disable CS9113 // Parameter is unread.
1211

@@ -20,13 +19,13 @@ public class AzureSqlEntityTableRepository_Tests(MsSqlDatabaseFixture fixture, I
2019
private readonly Random random = new();
2120
private List<AzureSqlEntityMovie> movies = [];
2221

23-
public async Task InitializeAsync()
22+
public async ValueTask InitializeAsync()
2423
{
2524
Context = await AzureSqlDbContext.CreateContextAsync(fixture.ConnectionString, output);
2625
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
2726
}
2827

29-
public async Task DisposeAsync()
28+
public async ValueTask DisposeAsync()
3029
{
3130
if (Context is not null)
3231
{
@@ -51,26 +50,26 @@ protected override Task<string> GetRandomEntityIdAsync(bool exists)
5150
=> Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
5251
#endregion
5352

54-
[SkippableFact]
53+
[Fact]
5554
public void EntityTableRepository_BadDbSet_Throws()
5655
{
57-
Skip.IfNot(CanRunLiveTests());
56+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
5857
Action act = () => _ = new EntityTableRepository<EntityTableData>(Context);
5958
act.Should().Throw<ArgumentException>();
6059
}
6160

62-
[SkippableFact]
61+
[Fact]
6362
public void EntityTableRepository_GoodDbSet_Works()
6463
{
65-
Skip.IfNot(CanRunLiveTests());
64+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
6665
Action act = () => _ = new EntityTableRepository<AzureSqlEntityMovie>(Context);
6766
act.Should().NotThrow();
6867
}
6968

70-
[SkippableFact]
69+
[Fact]
7170
public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUpdateExceptionThrown()
7271
{
73-
Skip.IfNot(CanRunLiveTests());
72+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
7473
EntityTableRepository<AzureSqlEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<AzureSqlEntityMovie>;
7574
string id = await GetRandomEntityIdAsync(true);
7675
AzureSqlEntityMovie expectedPayload = await GetEntityAsync(id);
@@ -81,10 +80,10 @@ public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUp
8180
(await act.Should().ThrowAsync<HttpException>()).WithStatusCode(409).And.WithPayload(expectedPayload);
8281
}
8382

84-
[SkippableFact]
83+
[Fact]
8584
public async Task WrapExceptionAsync_ThrowsRepositoryException_WhenDbUpdateExceptionThrown()
8685
{
87-
Skip.IfNot(CanRunLiveTests());
86+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
8887
EntityTableRepository<AzureSqlEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<AzureSqlEntityMovie>;
8988
string id = await GetRandomEntityIdAsync(true);
9089
AzureSqlEntityMovie expectedPayload = await GetEntityAsync(id);

tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
<PrivateAssets>all</PrivateAssets>
1414
</PackageReference>
1515
<PackageReference Include="Ulid" />
16-
<PackageReference Include="xRetry" />
1716
</ItemGroup>
1817

1918
<ItemGroup>

tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CosmosEntityTableRepository_Tests.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using CommunityToolkit.Datasync.TestCommon;
66
using CommunityToolkit.Datasync.TestCommon.Databases;
77
using Microsoft.EntityFrameworkCore;
8-
using Xunit.Abstractions;
98

109
#pragma warning disable CS9113 // Parameter is unread.
1110

@@ -19,7 +18,7 @@ public class CosmosEntityTableRepository_Tests(DatabaseFixture fixture, ITestOut
1918
private readonly Random random = new();
2019
private List<CosmosEntityMovie> movies = [];
2120

22-
public async Task InitializeAsync()
21+
public async ValueTask InitializeAsync()
2322
{
2423
if (!string.IsNullOrEmpty(ConnectionStrings.CosmosDb))
2524
{
@@ -28,7 +27,7 @@ public async Task InitializeAsync()
2827
}
2928
}
3029

31-
public async Task DisposeAsync()
30+
public async ValueTask DisposeAsync()
3231
{
3332
if (Context is not null)
3433
{
@@ -53,26 +52,26 @@ protected override Task<string> GetRandomEntityIdAsync(bool exists)
5352
=> Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
5453
#endregion
5554

56-
[SkippableFact]
55+
[Fact]
5756
public void EntityTableRepository_BadDbSet_Throws()
5857
{
59-
Skip.IfNot(CanRunLiveTests());
58+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
6059
Action act = () => _ = new EntityTableRepository<EntityTableData>(Context);
6160
act.Should().Throw<ArgumentException>();
6261
}
6362

64-
[SkippableFact]
63+
[Fact]
6564
public void EntityTableRepository_GoodDbSet_Works()
6665
{
67-
Skip.IfNot(CanRunLiveTests());
66+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
6867
Action act = () => _ = new EntityTableRepository<CosmosEntityMovie>(Context);
6968
act.Should().NotThrow();
7069
}
7170

72-
[SkippableFact]
71+
[Fact]
7372
public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUpdateExceptionThrown()
7473
{
75-
Skip.IfNot(CanRunLiveTests());
74+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
7675
EntityTableRepository<CosmosEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<CosmosEntityMovie>;
7776
string id = await GetRandomEntityIdAsync(true);
7877
CosmosEntityMovie expectedPayload = await GetEntityAsync(id);
@@ -83,10 +82,10 @@ public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUp
8382
(await act.Should().ThrowAsync<HttpException>()).WithStatusCode(409).And.WithPayload(expectedPayload);
8483
}
8584

86-
[SkippableFact]
85+
[Fact]
8786
public async Task WrapExceptionAsync_ThrowsRepositoryException_WhenDbUpdateExceptionThrown()
8887
{
89-
Skip.IfNot(CanRunLiveTests());
88+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
9089
EntityTableRepository<CosmosEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<CosmosEntityMovie>;
9190
string id = await GetRandomEntityIdAsync(true);
9291
CosmosEntityMovie expectedPayload = await GetEntityAsync(id);

tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/PgEntityTableRepository_Tests.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using CommunityToolkit.Datasync.TestCommon.Databases;
77
using CommunityToolkit.Datasync.TestCommon.Fixtures;
88
using Microsoft.EntityFrameworkCore;
9-
using Xunit.Abstractions;
109

1110
#pragma warning disable CS9113 // Parameter is unread.
1211

@@ -20,13 +19,13 @@ public class PgEntityTableRepository_Tests(PostgreSqlDatabaseFixture fixture, IT
2019
private readonly Random random = new();
2120
private List<PgEntityMovie> movies = [];
2221

23-
public async Task InitializeAsync()
22+
public async ValueTask InitializeAsync()
2423
{
2524
Context = await PgDbContext.CreateContextAsync(fixture.ConnectionString, output);
2625
this.movies = await Context.Movies.AsNoTracking().ToListAsync();
2726
}
2827

29-
public async Task DisposeAsync()
28+
public async ValueTask DisposeAsync()
3029
{
3130
if (Context is not null)
3231
{
@@ -51,26 +50,26 @@ protected override Task<string> GetRandomEntityIdAsync(bool exists)
5150
=> Task.FromResult(exists ? this.movies[this.random.Next(this.movies.Count)].Id : Guid.NewGuid().ToString());
5251
#endregion
5352

54-
[SkippableFact]
53+
[Fact]
5554
public void EntityTableRepository_BadDbSet_Throws()
5655
{
57-
Skip.IfNot(CanRunLiveTests());
56+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
5857
Action act = () => _ = new EntityTableRepository<EntityTableData>(Context);
5958
act.Should().Throw<ArgumentException>();
6059
}
6160

62-
[SkippableFact]
61+
[Fact]
6362
public void EntityTableRepository_GoodDbSet_Works()
6463
{
65-
Skip.IfNot(CanRunLiveTests());
64+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
6665
Action act = () => _ = new EntityTableRepository<PgEntityMovie>(Context);
6766
act.Should().NotThrow();
6867
}
6968

70-
[SkippableFact]
69+
[Fact]
7170
public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUpdateExceptionThrown()
7271
{
73-
Skip.IfNot(CanRunLiveTests());
72+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
7473
EntityTableRepository<PgEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<PgEntityMovie>;
7574
string id = await GetRandomEntityIdAsync(true);
7675
PgEntityMovie expectedPayload = await GetEntityAsync(id);
@@ -81,10 +80,10 @@ public async Task WrapExceptionAsync_ThrowsConflictException_WhenDbConcurrencyUp
8180
(await act.Should().ThrowAsync<HttpException>()).WithStatusCode(409).And.WithPayload(expectedPayload);
8281
}
8382

84-
[SkippableFact]
83+
[Fact]
8584
public async Task WrapExceptionAsync_ThrowsRepositoryException_WhenDbUpdateExceptionThrown()
8685
{
87-
Skip.IfNot(CanRunLiveTests());
86+
Assert.SkipUnless(CanRunLiveTests(), "Live tests are not enabled.");
8887
EntityTableRepository<PgEntityMovie> repository = await GetPopulatedRepositoryAsync() as EntityTableRepository<PgEntityMovie>;
8988
string id = await GetRandomEntityIdAsync(true);
9089
PgEntityMovie expectedPayload = await GetEntityAsync(id);

0 commit comments

Comments
 (0)