Skip to content
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
5 changes: 5 additions & 0 deletions .github/workflows/build-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ jobs:
name: coverage-report
path: artifacts/coverage

# The coverage run is on xunit.v3 3.x. This runs the xUnit tests again on 4.x, against
# DependencyModules.xUnit4.
- name: Test on xunit.v3 4.x
run: ./scripts/test-xunit.sh 4

# Packs and consumes the packages the way a real user would. Catches packaging faults
# that a ProjectReference-based test suite cannot see.
- name: Verify packages
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ jobs:
cat artifacts/coverage/SummaryGithub.md >> "$GITHUB_STEP_SUMMARY"
fi

- name: Test on xunit.v3 4.x
run: ./scripts/test-xunit.sh 4

- name: Verify packages
run: ./scripts/verify-packages.sh "${{ steps.version.outputs.version }}"

Expand All @@ -86,6 +89,7 @@ jobs:
src/DependencyModules.SourceGenerator.Impl/DependencyModules.SourceGenerator.Impl.csproj \
src/DependencyModules.Testing/DependencyModules.Testing.csproj \
src/DependencyModules.xUnit/DependencyModules.xUnit.csproj \
src/DependencyModules.xUnit4/DependencyModules.xUnit4.csproj \
src/DependencyModules.NUnit/DependencyModules.NUnit.csproj \
src/DependencyModules.NSubstitute/DependencyModules.NSubstitute.csproj \
src/DependencyModules.Moq/DependencyModules.Moq.csproj \
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/xunit-prerelease.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: xunit-prerelease

# Runs the xUnit tests against the newest xunit.v3 on nuget.org, prereleases included. xUnit ships
# prereleases of a major for months before the release, and the 4.0 breaks were in them: the first
# on 2026-04-12, four months before 4.0.0. A failure here is notice of a coming break. Without it,
# the first report comes from a consumer.
on:
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# See build-package.yaml: the 10.0 SDK builds, the 8.0 runtime runs the net8.0 tests.
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x

- name: Find the newest xunit.v3 and runner
id: versions
run: |
newest() {
curl -fsSL "https://api.nuget.org/v3-flatcontainer/$1/index.json" | python3 -c '
import json, sys

def precedence(version):
release, _, prerelease = version.partition("-")
numbers = [int(part) for part in release.split(".")]
if not prerelease:
return (numbers, 1, [])
return (numbers, 0, [(0, int(part), "") if part.isdigit() else (1, 0, part) for part in prerelease.split(".")])

print(max(json.load(sys.stdin)["versions"], key=precedence))
'
}
echo "xunit=$(newest xunit.v3.mtp-off)" >> "$GITHUB_OUTPUT"
echo "runner=$(newest xunit.runner.visualstudio)" >> "$GITHUB_OUTPUT"

# 4 is the newest DependencyModules xUnit build. Raise it when a build for a new major is added.
- name: Test on xunit.v3.mtp-off ${{ steps.versions.outputs.xunit }}
run: ./scripts/test-xunit.sh 4 "${{ steps.versions.outputs.xunit }}" "${{ steps.versions.outputs.runner }}"
19 changes: 15 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ To run all tests with code coverage, use the coverage script. The script writes
./scripts/coverage.sh 85
```

To do a test of the packages that a user gets, use the package script. The script packs the nine packages. Then it builds and runs a test application for each target framework. The test application references these packages.
The xUnit test projects use `xunit.v3` version 3 and `DependencyModules.xUnit`. To run them with `xunit.v3` version 4 and `DependencyModules.xUnit4`, use the xUnit script:

```shell
./scripts/test-xunit.sh 4
```

The `XunitMajor` property in `Directory.Build.props` selects the version. The script sets it to the major version that you give.

To do a test of the packages that a user gets, use the package script. The script packs the ten packages. Then it builds and runs a test application for each target framework. The test application references these packages.

```shell
./scripts/verify-packages.sh
Expand Down Expand Up @@ -63,7 +71,10 @@ The `build-package` workflow runs for each pull request to `main`. It does these
1. It does a check of the format with CSharpier.
2. It builds the solution.
3. It runs all tests with code coverage. The line coverage must be 85 percent or more.
4. It runs `scripts/verify-packages.sh`.
4. It runs the xUnit tests again with `xunit.v3` version 4.
5. It runs `scripts/verify-packages.sh`.

The `xunit-prerelease` workflow runs each week. It runs the xUnit tests with the newest `xunit.v3` on nuget.org, prereleases included. A failure tells you about a change in xUnit before its release.

## Documentation

Expand Down Expand Up @@ -96,8 +107,8 @@ git push origin v1.5.0
The `release` workflow then does these steps:

1. It builds the code.
2. It runs the tests.
3. It packs the nine packages.
2. It runs the tests with `xunit.v3` version 3 and version 4.
3. It packs the ten packages.
4. It publishes the packages to nuget.org and to GitHub Packages.
5. It makes a GitHub release with generated release notes.

Expand Down
15 changes: 15 additions & 0 deletions DependencyModules.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyModules.NUnit", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SutProject.NUnitTests", "integ-tests\SutProject.NUnitTests\SutProject.NUnitTests.csproj", "{C3B5B8CE-D219-42C8-A1B8-5C283C053E98}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyModules.xUnit4", "src\DependencyModules.xUnit4\DependencyModules.xUnit4.csproj", "{D1AEA1C9-CD76-4664-956B-706962E78E3D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -272,6 +274,18 @@ Global
{C3B5B8CE-D219-42C8-A1B8-5C283C053E98}.Release|x64.Build.0 = Release|Any CPU
{C3B5B8CE-D219-42C8-A1B8-5C283C053E98}.Release|x86.ActiveCfg = Release|Any CPU
{C3B5B8CE-D219-42C8-A1B8-5C283C053E98}.Release|x86.Build.0 = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|x64.ActiveCfg = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|x64.Build.0 = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|x86.ActiveCfg = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Debug|x86.Build.0 = Debug|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|Any CPU.Build.0 = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|x64.ActiveCfg = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|x64.Build.0 = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|x86.ActiveCfg = Release|Any CPU
{D1AEA1C9-CD76-4664-956B-706962E78E3D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -296,5 +310,6 @@ Global
{DB6B8603-CBA7-443D-AEA9-E37BF61AD1B7} = {FB161494-E422-4D4E-BA04-3473C2EB13B0}
{F911E2B7-4D5A-4DC2-9E3E-4B2CFC30CF4E} = {FB161494-E422-4D4E-BA04-3473C2EB13B0}
{C3B5B8CE-D219-42C8-A1B8-5C283C053E98} = {DC7C95FE-F58F-4F16-8DFA-87E1B67DCEAE}
{D1AEA1C9-CD76-4664-956B-706962E78E3D} = {FB161494-E422-4D4E-BA04-3473C2EB13B0}
EndGlobalSection
EndGlobal
25 changes: 25 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@
<LibraryTargetFrameworks>net8.0;net10.0</LibraryTargetFrameworks>
</PropertyGroup>

<!--
The xunit.v3 major that the xUnit test projects build and run against. CI runs both, through
scripts/test-xunit.sh for the second. 4 uses xunit.v3.mtp-off, not xunit.v3: the
Microsoft.Testing.Platform v2 that xunit.v3 4.x brings refuses the VSTest `dotnet test` on
the .NET 10 SDK, and xUnit ships mtp-off for exactly that. XunitV3Version and
XunitRunnerVersion can be set from the command line, which the prerelease workflow does.
-->
<PropertyGroup>
<XunitMajor Condition="'$(XunitMajor)' == ''">3</XunitMajor>
</PropertyGroup>

<PropertyGroup Condition="'$(XunitMajor)' == '3'">
<XunitV3Package>xunit.v3</XunitV3Package>
<XunitV3Version Condition="'$(XunitV3Version)' == ''">3.2.2</XunitV3Version>
<XunitRunnerVersion Condition="'$(XunitRunnerVersion)' == ''">3.1.5</XunitRunnerVersion>
<DependencyModulesXunitProject>DependencyModules.xUnit</DependencyModulesXunitProject>
</PropertyGroup>

<PropertyGroup Condition="'$(XunitMajor)' == '4'">
<XunitV3Package>xunit.v3.mtp-off</XunitV3Package>
<XunitV3Version Condition="'$(XunitV3Version)' == ''">4.0.1</XunitV3Version>
<XunitRunnerVersion Condition="'$(XunitRunnerVersion)' == ''">4.0.0</XunitRunnerVersion>
<DependencyModulesXunitProject>DependencyModules.xUnit4</DependencyModulesXunitProject>
</PropertyGroup>

<PropertyGroup>
<Authors>Ian Johnson</Authors>
<Company>Ian Johnson</Company>
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ The `integ-tests` folder contains projects that reference the source projects of
| --- | --- |
| [DependencyModules.Runtime](https://www.nuget.org/packages/DependencyModules.Runtime/) | The attributes, the interfaces, and the `AddModule` methods. |
| [DependencyModules.SourceGenerator](https://www.nuget.org/packages/DependencyModules.SourceGenerator/) | The source generator. It operates only when you compile. |
| [DependencyModules.xUnit](https://www.nuget.org/packages/DependencyModules.xUnit/) | `[ModuleTest]` for xUnit v3. |
| [DependencyModules.xUnit](https://www.nuget.org/packages/DependencyModules.xUnit/) | `[ModuleTest]` for xUnit v3, with `xunit.v3` version 3. |
| [DependencyModules.xUnit4](https://www.nuget.org/packages/DependencyModules.xUnit4/) | `[ModuleTest]` for xUnit v3, with `xunit.v3` version 4. |
| [DependencyModules.NUnit](https://www.nuget.org/packages/DependencyModules.NUnit/) | `[ModuleTest]` and `[ModuleTestCase]` for NUnit 4. |
| [DependencyModules.Testing](https://www.nuget.org/packages/DependencyModules.Testing/) | The test attributes and interfaces that the xUnit and NUnit packages use. |
| [DependencyModules.Testing](https://www.nuget.org/packages/DependencyModules.Testing/) | The test attributes and interfaces that the xUnit and NUnit packages use. It also gives the test that runs, and a logger that writes to the output of the test. |
| [DependencyModules.NSubstitute](https://www.nuget.org/packages/DependencyModules.NSubstitute/) | `[Mock]` parameters with NSubstitute. |
| [DependencyModules.Moq](https://www.nuget.org/packages/DependencyModules.Moq/) | `[Mock]` and `Mock<T>` parameters with Moq. |
| [DependencyModules.FakeItEasy](https://www.nuget.org/packages/DependencyModules.FakeItEasy/) | `[Mock]` parameters with FakeItEasy. |
Expand Down
101 changes: 101 additions & 0 deletions integ-tests/SutProject.NUnitTests/CurrentTestTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using DependencyModules.NUnit.Attributes;
using DependencyModules.Testing.Attributes.Interfaces;
using DependencyModules.Testing.Impl;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using NUnit.Framework.Internal;

namespace SutProject.NUnitTests;

/// <summary>
/// <c>CurrentTest</c> under NUnit, where the runner package answers from
/// <c>TestExecutionContext.CurrentContext</c>.
/// </summary>
public class CurrentTestTests
{
private static readonly List<object?> RepeatKeys = [];

[ModuleTest]
public void KeyIsNUnitsObjectForTheRunningTest() =>
Assert.That(CurrentTest.Key, Is.SameAs(TestExecutionContext.CurrentContext.CurrentTest));

[ModuleTest]
public void DisplayNameIsTheFullNameOfTheTest() =>
Assert.That(CurrentTest.DisplayName, Is.EqualTo(TestContext.CurrentContext.Test.FullName));

[ModuleTest]
public void AssemblyIsTheAssemblyOfTheTestClass() =>
Assert.That(CurrentTest.Assembly, Is.SameAs(typeof(CurrentTestTests).Assembly));

[ModuleTest]
public void ALineIsWrittenToTheOutputOfTheTest()
{
Assert.That(CurrentTest.TryWriteLine("written through CurrentTest"), Is.True);

Assert.That(
TestExecutionContext.CurrentContext.CurrentResult.Output,
Does.Contain("written through CurrentTest")
);
}

[ModuleTest]
public void TheLoggerProviderWritesToTheOutputOfTheTest()
{
new TestOutputLoggerProvider()
.CreateLogger("Shop.OrderService")
.LogInformation("Order {Id} accepted", 42);

Assert.That(
TestExecutionContext.CurrentContext.CurrentResult.Output,
Does.Contain("info: Shop.OrderService[0] Order 42 accepted")
);
}

/// <summary>
/// The container is built inside the iteration, so the test is already running in the hooks
/// that build it. Under xUnit it is not; the documentation of CurrentTest gives both.
/// </summary>
[ModuleTest]
[RecordCurrentTestAtStartup]
public void TheTestIsRunningWhileTheContainerIsBuilt()
{
Assert.That(RecordCurrentTestAtStartupAttribute.Key, Is.SameAs(CurrentTest.Key));
Assert.That(
TestExecutionContext.CurrentContext.CurrentResult.Output,
Does.Contain("written while the container is built")
);
}

/// <summary>
/// NUnit makes no object of its own for one iteration, so the key is the test's for all of
/// them. The documentation of CurrentTest says so; the container is still one per iteration.
/// </summary>
[ModuleTest]
[Repeat(2)]
public void TheIterationsOfARepeatedTestShareOneKey()
{
RepeatKeys.Add(CurrentTest.Key);

Assert.That(CurrentTest.Key, Is.Not.Null);
Assert.That(
RepeatKeys,
Has.Count.EqualTo(TestExecutionContext.CurrentContext.CurrentRepeatCount + 1)
);
Assert.That(RepeatKeys, Is.All.SameAs(RepeatKeys[0]));
}
}

[AttributeUsage(AttributeTargets.Method)]
public sealed class RecordCurrentTestAtStartupAttribute : Attribute, ITestStartupAttribute
{
public static object? Key { get; private set; }

public Task StartupAsync(ITestMethodContext testMethod, IServiceProvider serviceProvider)
{
Key = CurrentTest.Key;

CurrentTest.TryWriteLine("written while the container is built");

return Task.CompletedTask;
}
}
7 changes: 4 additions & 3 deletions integ-tests/SutProject.Tests/SutProject.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ProjectReference Include="..\..\src\DependencyModules.NSubstitute\DependencyModules.NSubstitute.csproj" />
<ProjectReference Include="..\..\src\DependencyModules.Moq\DependencyModules.Moq.csproj" />
<ProjectReference Include="..\..\src\DependencyModules.FakeItEasy\DependencyModules.FakeItEasy.csproj" />
<ProjectReference Include="..\..\src\DependencyModules.xUnit\DependencyModules.xUnit.csproj" />
<ProjectReference Include="..\..\src\$(DependencyModulesXunitProject)\$(DependencyModulesXunitProject).csproj"/>
<ProjectReference Include="..\SecondarySutProject\SecondarySutProject.csproj"/>
<ProjectReference Include="..\SutProject\SutProject.csproj"/>
<ProjectReference Include="..\..\src\DependencyModules.SourceGenerator\DependencyModules.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
Expand All @@ -24,11 +24,12 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVersion)">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.v3" Version="3.2.2"/>
<!-- XunitMajor in Directory.Build.props selects the xunit.v3 major and the DependencyModules project. -->
<PackageReference Include="$(XunitV3Package)" Version="$(XunitV3Version)"/>
</ItemGroup>

</Project>
Loading
Loading