Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ To have Nx wrap it anyway, opt the target in:

### Test project detection

A project gets a `test` target, and loses its `pack` target, when either MSBuild property is `true`:
A project gets a `test` target, and loses its `pack`, `publish`, and `run` targets, when either MSBuild property is `true`:

- `IsTestProject`, which `Microsoft.NET.Test.Sdk` sets.
- `IsTestingPlatformApplication`, which [Microsoft.Testing.Platform](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-platform-intro) sets.
Expand All @@ -164,7 +164,7 @@ These are the two properties the .NET SDK itself checks before it will run a pro
Nx therefore agrees with `dotnet test` about what a test project is.
Referencing a framework such as xunit or NUnit is not enough on its own, which is what lets a
library expose test helpers and keep its `pack` target.
`publish` and `run` depend only on `OutputType`, so a runner built as an executable keeps both.
A test project built as an executable still gets no `publish` or `run` target.

Both properties arrive with a restored package, so neither has a value before the project has
been restored.
Expand Down Expand Up @@ -256,10 +256,10 @@ Once a .NET project file has been identified, the targets are created with the c
- `test` - Runs unit tests (for test projects)
- `clean` - Removes build outputs
- `restore` - Restores NuGet package dependencies. Not created unless you opt in with `"restore": true`
- `publish` - Publishes the application (for executable projects)
- `publish` - Publishes the application (for executable projects that aren't test projects)
- `pack` - Creates a NuGet package (for library projects)
- `watch` - Watches the project for changes and rebuilds (automatically marked as continuous)
- `run` - Runs the application (for executable projects)
- `run` - Runs the application (for executable projects that aren't test projects)

Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/dotnet`.

Expand Down
2 changes: 2 additions & 0 deletions e2e/dotnet/src/dotnet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ describe('.NET Plugin', () => {

expect(details.targets).toHaveProperty('build');
expect(details.targets).toHaveProperty('test');
expect(details.targets).not.toHaveProperty('publish');
expect(details.targets).not.toHaveProperty('run');
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using MsbuildAnalyzer.Models;
using MsbuildAnalyzer.Utilities;
using Xunit;

namespace MsbuildAnalyzer.Tests;

/// <summary>
/// Which projects get <c>publish</c> and <c>run</c>. Test projects evaluate to
/// OutputType=Exe, since Microsoft.NET.Test.Sdk sets it on .NET Core and MTP
/// runners set it themselves, but they are not applications.
/// </summary>
public class TargetBuilderExecutableTargetsTests
{
private static readonly string WorkspaceRoot = Path.Combine(Path.GetTempPath(), "nx-dotnet-ws");

private static Dictionary<string, Target> BuildTargets(bool isExe, bool isTest) =>
TargetBuilder.BuildTargets(
projectName: "MyProj",
fileName: "MyProj.csproj",
isTest: isTest,
isExe: isExe,
packageRefs: new List<PackageReference>(),
properties: new Dictionary<string, string>(),
projectDirectory: Path.Combine(WorkspaceRoot, "apps", "MyProj"),
workspaceRoot: WorkspaceRoot,
options: new PluginOptions(),
nxJson: null,
directoryBuildInputs: new List<string>());

[Fact]
public void Executable_GetsPublishAndRun()
{
var targets = BuildTargets(isExe: true, isTest: false);

Assert.Contains("publish", targets.Keys);
Assert.Contains("run", targets.Keys);
}

[Fact]
public void ExecutableTestProject_GetsNoPublishOrRun()
{
var targets = BuildTargets(isExe: true, isTest: true);

Assert.Contains("test", targets.Keys);
Assert.DoesNotContain("publish", targets.Keys);
Assert.DoesNotContain("run", targets.Keys);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ namespace MsbuildAnalyzer.Tests;

/// <summary>
/// What makes a project a test project, and so which projects get a <c>test</c>
/// target and lose <c>pack</c>. <c>publish</c> and <c>run</c> are decided by
/// OutputType alone, so an MTP runner keeps both.
/// target and lose <c>pack</c>, <c>publish</c> and <c>run</c>.
///
/// The expectations here mirror the SDK's own gate in
/// <c>Microsoft.TestPlatform.ImportAfter.targets</c>:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ private static void AddPublishTarget(
Dictionary<string, Target> targets,
string projectName,
string fileName,
bool isTest,
EvaluatedProperties properties,
string projectDirectory,
string workspaceRoot,
Expand Down
4 changes: 2 additions & 2 deletions packages/dotnet/analyzer/Utilities/TargetBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public static Dictionary<string, Target> BuildTargets(
AddCleanTarget(targets, fileName, isTest, options);
AddWatchTarget(targets, fileName, options);

if (isExe)
if (isExe && !isTest)
{
AddPublishTarget(targets, projectName, fileName, isTest, properties, projectDirectory, workspaceRoot, options, productionInput, directoryBuildInputs);
AddPublishTarget(targets, projectName, fileName, properties, projectDirectory, workspaceRoot, options, productionInput, directoryBuildInputs);
AddRunTarget(targets, fileName, options);
}

Expand Down
Loading