Skip to content

Commit cdd1436

Browse files
Add xunit.v3 implementation (#7612)
* Add Akka.TestKit.Xunit3 implementation * Refactor Akka.TestKit.Xunit3 to Akka.TestKit.Xunit --------- Co-authored-by: Aaron Stannard <[email protected]>
1 parent db93be5 commit cdd1436

17 files changed

+802
-429
lines changed

Akka.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClusterToolsExample.Node",
277277
EndProject
278278
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.TestKit.Xunit2.Tests", "src\contrib\testkits\Akka.TestKit.Xunit2.Tests\Akka.TestKit.Xunit2.Tests.csproj", "{95017C99-E960-44E5-83AD-BF21461DF06F}"
279279
EndProject
280+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.TestKit.Xunit.Tests", "src\contrib\testkits\Akka.TestKit.Xunit.Tests\Akka.TestKit.Xunit.Tests.csproj", "{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}"
281+
EndProject
280282
Global
281283
GlobalSection(SolutionConfigurationPlatforms) = preSolution
282284
Debug|Any CPU = Debug|Any CPU
@@ -1304,6 +1306,18 @@ Global
13041306
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x64.Build.0 = Release|Any CPU
13051307
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x86.ActiveCfg = Release|Any CPU
13061308
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x86.Build.0 = Release|Any CPU
1309+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1310+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|Any CPU.Build.0 = Debug|Any CPU
1311+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|x64.ActiveCfg = Debug|Any CPU
1312+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|x64.Build.0 = Debug|Any CPU
1313+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|x86.ActiveCfg = Debug|Any CPU
1314+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Debug|x86.Build.0 = Debug|Any CPU
1315+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|Any CPU.ActiveCfg = Release|Any CPU
1316+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|Any CPU.Build.0 = Release|Any CPU
1317+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x64.ActiveCfg = Release|Any CPU
1318+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x64.Build.0 = Release|Any CPU
1319+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x86.ActiveCfg = Release|Any CPU
1320+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68}.Release|x86.Build.0 = Release|Any CPU
13071321
EndGlobalSection
13081322
GlobalSection(SolutionProperties) = preSolution
13091323
HideSolutionNode = FALSE
@@ -1426,6 +1440,7 @@ Global
14261440
{ED00E6F4-2B5C-4F16-ADE4-45E4A73C17B8} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
14271441
{337A85B5-4A7C-4883-8634-46E7E52A765F} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
14281442
{95017C99-E960-44E5-83AD-BF21461DF06F} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E}
1443+
{F80F41E6-E5C7-4C92-B1CF-42539ECFBE68} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E}
14291444
EndGlobalSection
14301445
GlobalSection(ExtensibilityGlobals) = postSolution
14311446
SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164}

Directory.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
</PropertyGroup>
2020
<PropertyGroup>
2121
<XunitVersion>2.8.1</XunitVersion>
22+
<Xunit3Version>2.0.1</Xunit3Version>
23+
<Xunit3RunnerVersion>3.0.2</Xunit3RunnerVersion>
2224
<XunitRunnerVersion>2.8.1</XunitRunnerVersion>
2325
<TestSdkVersion>17.9.0</TestSdkVersion>
2426
<HyperionVersion>0.12.2</HyperionVersion>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="../../../xunitSettings.props" />
3+
4+
<PropertyGroup>
5+
<TargetFrameworks>$(NetFrameworkTestVersion);net8.0</TargetFrameworks>
6+
<OutputType>Exe</OutputType>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
10+
<IsPackable>false</IsPackable>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
15+
<PackageReference Include="xunit.v3" Version="$(Xunit3Version)" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="$(Xunit3RunnerVersion)">
17+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
18+
<PrivateAssets>all</PrivateAssets>
19+
</PackageReference>
20+
<PackageReference Include="coverlet.collector" Version="3.1.2">
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
22+
<PrivateAssets>all</PrivateAssets>
23+
</PackageReference>
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<ProjectReference Include="..\Akka.TestKit.Xunit\Akka.TestKit.Xunit.csproj" />
28+
</ItemGroup>
29+
30+
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="AkkaEqualExceptionSpec.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
//-----------------------------------------------------------------------
7+
8+
using Akka.TestKit.Xunit.Internals;
9+
using Xunit;
10+
11+
namespace Akka.TestKit.Xunit.Tests.Internals;
12+
13+
public static class AkkaEqualExceptionSpec
14+
{
15+
#if NETFRAMEWORK
16+
[Fact]
17+
public static void Constructor_deserializes_message()
18+
{
19+
var originalException = new AkkaEqualException("Test message");
20+
21+
AkkaEqualException deserializedException;
22+
using (var memoryStream = new System.IO.MemoryStream())
23+
{
24+
var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
25+
formatter.Serialize(memoryStream, originalException);
26+
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
27+
deserializedException = (AkkaEqualException)formatter.Deserialize(memoryStream);
28+
}
29+
30+
Assert.Equal(originalException.Message, deserializedException.Message);
31+
}
32+
#endif
33+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="XunitAssertionsSpec.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
//-----------------------------------------------------------------------
7+
8+
using Akka.TestKit.Xunit;
9+
using Xunit;
10+
using Xunit.Sdk;
11+
12+
namespace Akka.TestKit.Xunit.Tests;
13+
14+
public class XunitAssertionsSpec
15+
{
16+
private readonly XunitAssertions _assertions = new();
17+
18+
[Fact]
19+
public void Assert_does_not_format_message_when_no_arguments_are_specified()
20+
{
21+
const string testMessage = "{Value} with different format placeholders {0}";
22+
23+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage));
24+
Assert.Contains(testMessage, exception.Message);
25+
26+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage));
27+
Assert.Contains(testMessage, exception.Message);
28+
29+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage));
30+
Assert.Contains(testMessage, exception.Message);
31+
32+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage));
33+
Assert.Contains(testMessage, exception.Message);
34+
35+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage));
36+
Assert.Contains(testMessage, exception.Message);
37+
}
38+
39+
[Fact]
40+
public void Assert_formats_message_when_arguments_are_specified()
41+
{
42+
const string testMessage = "Meaning: {0}";
43+
const string expectedMessage = "Meaning: 42";
44+
45+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage, 42));
46+
Assert.Contains(expectedMessage, exception.Message);
47+
48+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage, 42));
49+
Assert.Contains(expectedMessage, exception.Message);
50+
51+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage, 42));
52+
Assert.Contains(expectedMessage, exception.Message);
53+
54+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage, 42));
55+
Assert.Contains(expectedMessage, exception.Message);
56+
57+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage, 42));
58+
Assert.Contains(expectedMessage, exception.Message);
59+
}
60+
61+
[Fact]
62+
public void Assert_catches_format_exceptions()
63+
{
64+
const string testMessage = "Meaning: {0} {1}";
65+
const string expectedMessage = "Could not string.Format";
66+
67+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage, 42));
68+
Assert.Contains(expectedMessage, exception.Message);
69+
70+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage, 42));
71+
Assert.Contains(expectedMessage, exception.Message);
72+
73+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage, 42));
74+
Assert.Contains(expectedMessage, exception.Message);
75+
76+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage, 42));
77+
Assert.Contains(expectedMessage, exception.Message);
78+
79+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage, 42));
80+
Assert.Contains(expectedMessage, exception.Message);
81+
}
82+
}

src/contrib/testkits/Akka.TestKit.Xunit/Akka.TestKit.Xunit.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
<PropertyGroup>
44
<Description>TestKit for writing tests for Akka.NET using xUnit.</Description>
5-
<TargetFrameworks>$(NetStandardLibVersion);$(NetLibVersion)</TargetFrameworks>
5+
<TargetFrameworks>$(NetStandardLibVersion);net8.0</TargetFrameworks>
6+
<Nullable>enable</Nullable>
67
<PackageTags>$(AkkaPackageTags);testkit;xunit</PackageTags>
78
<GenerateDocumentationFile>true</GenerateDocumentationFile>
89
</PropertyGroup>
910

1011
<ItemGroup>
1112
<ProjectReference Include="..\..\..\core\Akka.TestKit\Akka.TestKit.csproj" />
12-
<PackageReference Include="xunit" Version="$(XunitVersion)" />
13+
<PackageReference Include="xunit.v3.extensibility.core" Version="$(Xunit3Version)" />
14+
<PackageReference Include="xunit.v3.assert" Version="$(Xunit3Version)" />
1315
</ItemGroup>
1416

1517
<PropertyGroup>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="LocalFactAttribute.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
//-----------------------------------------------------------------------
7+
8+
using System;
9+
using Xunit;
10+
using Xunit.v3;
11+
12+
namespace Akka.TestKit.Xunit.Attributes;
13+
14+
/// <summary>
15+
/// <para>
16+
/// This custom XUnit Fact attribute will skip unit tests if the environment variable
17+
/// "XUNIT_SKIP_LOCAL_FACT" exists and is set to the string "true"
18+
/// </para>
19+
/// <para>
20+
/// Note that the original <see cref="Skip"/> property takes precedence over this attribute,
21+
/// any unit tests with <see cref="LocalFactAttribute"/> with its <see cref="Skip"/> property
22+
/// set will always be skipped, regardless of the environment variable content.
23+
/// </para>
24+
/// </summary>
25+
[XunitTestCaseDiscoverer(typeof(FactDiscoverer))]
26+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
27+
public class LocalFactAttribute: Attribute, IFactAttribute
28+
{
29+
private const string EnvironmentVariableName = "XUNIT_SKIP_LOCAL_FACT";
30+
31+
private string? _skip;
32+
33+
/// <inheritdoc />
34+
public string? DisplayName { get; set; }
35+
36+
/// <inheritdoc/>
37+
public bool Explicit { get; set; }
38+
39+
/// <inheritdoc/>
40+
public Type[]? SkipExceptions { get; set; }
41+
42+
/// <inheritdoc/>
43+
public Type? SkipType { get; set; }
44+
45+
/// <inheritdoc/>
46+
public string? SkipUnless { get; set; }
47+
48+
/// <inheritdoc/>
49+
public string? SkipWhen { get; set; }
50+
51+
/// <inheritdoc/>
52+
public int Timeout { get; set; }
53+
54+
/// <inheritdoc/>
55+
public string? Skip
56+
{
57+
get
58+
{
59+
var skipLocal = Environment.GetEnvironmentVariable(EnvironmentVariableName)?
60+
.ToLowerInvariant();
61+
return skipLocal is "true" ? SkipLocal ?? "Local facts are being skipped" : _skip;
62+
}
63+
set => _skip = value;
64+
}
65+
66+
/// <summary>
67+
/// The reason why this unit test is being skipped by the <see cref="LocalFactAttribute"/>.
68+
/// Note that the original <see cref="FactAttribute.Skip"/> property takes precedence over this message.
69+
/// </summary>
70+
public string? SkipLocal { get; set; }
71+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="LocalTheoryAttribute.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
//-----------------------------------------------------------------------
7+
8+
using System;
9+
using Xunit.v3;
10+
11+
namespace Akka.TestKit.Xunit.Attributes
12+
{
13+
/// <summary>
14+
/// <para>
15+
/// This custom XUnit Fact attribute will skip unit tests if the environment variable
16+
/// "XUNIT_SKIP_LOCAL_THEORY" exists and is set to the string "true"
17+
/// </para>
18+
/// <para>
19+
/// Note that the original <see cref="IFactAttribute.Skip"/> property takes precedence over this attribute,
20+
/// any unit tests with <see cref="LocalTheoryAttribute"/> with its <see cref="IFactAttribute.Skip"/> property
21+
/// set will always be skipped, regardless of the environment variable content.
22+
/// </para>
23+
/// </summary>
24+
[XunitTestCaseDiscoverer(typeof(TheoryDiscoverer))]
25+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
26+
public class LocalTheoryAttribute : LocalFactAttribute, ITheoryAttribute
27+
{
28+
/// <inheritdoc />
29+
public bool DisableDiscoveryEnumeration { get; set; }
30+
31+
/// <inheritdoc />
32+
public bool SkipTestWithoutData { get; set; }
33+
}
34+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="WindowsFactAttribute.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
//-----------------------------------------------------------------------
7+
8+
using System;
9+
using Xunit.v3;
10+
11+
namespace Akka.TestKit.Xunit.Attributes
12+
{
13+
/// <summary>
14+
/// <para>
15+
/// This custom XUnit Fact attribute will skip unit tests if the run-time environment is not windows
16+
/// </para>
17+
/// <para>
18+
/// Note that the original <see cref="Skip"/> property takes precedence over this attribute,
19+
/// any unit tests with <see cref="WindowsFactAttribute"/> with its <see cref="Skip"/> property
20+
/// set will always be skipped, regardless of the environment variable content.
21+
/// </para>
22+
/// </summary>
23+
public class WindowsFactAttribute : Attribute, IFactAttribute
24+
{
25+
private string? _skip;
26+
27+
28+
/// <inheritdoc />
29+
public string? DisplayName { get; set; }
30+
31+
/// <inheritdoc/>
32+
public bool Explicit { get; set; }
33+
34+
/// <inheritdoc/>
35+
public Type[]? SkipExceptions { get; set; }
36+
37+
/// <inheritdoc/>
38+
public Type? SkipType { get; set; }
39+
40+
/// <inheritdoc/>
41+
public string? SkipUnless { get; set; }
42+
43+
/// <inheritdoc/>
44+
public string? SkipWhen { get; set; }
45+
46+
/// <inheritdoc/>
47+
public int Timeout { get; set; }
48+
49+
/// <inheritdoc/>
50+
public string? Skip
51+
{
52+
get
53+
{
54+
if (_skip != null)
55+
return _skip;
56+
57+
var platform = Environment.OSVersion.Platform;
58+
var notWindows = platform is PlatformID.MacOSX or PlatformID.Unix or PlatformID.Xbox;
59+
return notWindows ? SkipUnix ?? "Skipped under Unix platforms" : null;
60+
}
61+
set => _skip = value;
62+
}
63+
64+
/// <summary>
65+
/// The reason why this unit test is being skipped by the <see cref="WindowsFactAttribute"/>.
66+
/// Note that the original <see cref="Skip"/> property takes precedence over this message.
67+
/// </summary>
68+
public string? SkipUnix { get; set; }
69+
}
70+
}
71+

0 commit comments

Comments
 (0)