Project Naming Conventions
Project Naming Conventions
Section titled “Project Naming Conventions”This page explains the mechanics behind the higher-level guidance in Engineering Principles. Read that page first when you need the policy and tradeoffs; use this page when you need the exact naming and layout rules the SDK implements.
The SDK applies several conventions automatically based on the .csproj filename and
NamespacePrefix.
Defaults (no extra configuration)
Section titled “Defaults (no extra configuration)”RootNamespace is always derived from $(NamespacePrefix).$(ProjectName) and is the canonical
default public name. By default (EnableAssemblyNameGeneration=true), AssemblyName and PackageId
both follow the fully evaluated RootNamespace. Test projects retain their detected suffix so test
assemblies stay distinct from the source assembly. Set EnableAssemblyNameGeneration=false (before the
SDK import) to opt out and use standard .NET behaviour (the .csproj filename):
.csproj filename | AssemblyName / PackageId | RootNamespace | Detected as |
|---|---|---|---|
Api.csproj | Acme.Api | Acme.Api | Source project |
Api.UnitTests.csproj | Acme.Api.UnitTests | Acme.Api | IsTestProject=true, TestingType=Unit |
Api.IntegrationTests.csproj | Acme.Api.IntegrationTests | Acme.Api | IsTestProject=true, TestingType=Integration |
SharedTestingFramework.csproj | Acme.SharedTestingFramework | Acme | IsSharedTestingProject=true |
Note:
InternalsVisibleTofollows$(AssemblyName)— so forApi.csprojthe SDK generatesAcme.Api.UnitTests,Acme.Api.IntegrationTests, etc.
Use short .csproj names — the SDK handles the prefixing:
✅ Api.csproj → short name, SDK resolves the rest❌ Acme.Api.csproj → redundant prefix, avoidA build-time check (PurviewProjectFileNameMismatch) enforces that the .csproj filename matches its
parent directory name, preventing inconsistent naming. Set DisableProjectFileNamingConventionCheck=true
to opt out.
Recommended structure: src/ + tests/
Section titled “Recommended structure: src/ + tests/”For larger repos, separate source and test projects into src/ and tests/ folders:
MyRepo/├── Directory.Build.props ← NamespacePrefix=Acme├── Directory.Build.targets├── Directory.Packages.props├── global.json├── src/│ ├── Api/│ │ └── Api.csproj│ ├── Core/│ │ └── Core.csproj│ └── SourceGenerator/│ └── SourceGenerator.csproj├── tests/│ ├── Api.UnitTests/│ │ └── Api.UnitTests.csproj → IsTestProject=true, TestingType=Unit│ ├── Api.IntegrationTests/│ │ └── Api.IntegrationTests.csproj│ └── SharedTestingFramework/│ └── SharedTestingFramework.csproj → IsSharedTestingProject=true└── package.jsonFlat structure: everything together
Section titled “Flat structure: everything together”For smaller repos, source and test projects can live side-by-side:
MyRepo/├── Directory.Build.props├── Directory.Build.targets├── Directory.Packages.props├── global.json├── Api/│ └── Api.csproj├── Api.UnitTests/│ └── Api.UnitTests.csproj├── Core/│ └── Core.csproj├── Core.IntegrationTests/│ └── Core.IntegrationTests.csproj└── package.jsonBoth layouts work identically — the SDK detects test projects by name suffix, not folder location.
Quick reference
Section titled “Quick reference”# Create a source projectmkdir src/Api && cd src/Apidotnet new classlib -n Api
# Create its unit testsmkdir ../../tests/Api.UnitTests && cd ../../tests/Api.UnitTestsdotnet new classlib -n Api.UnitTests # SDK wires TUnit automatically
# Or flat:mkdir Api.UnitTests && cd Api.UnitTestsdotnet new classlib -n Api.UnitTestsTest project naming conventions
Section titled “Test project naming conventions”Test projects are automatically detected by their suffix. The common recommended set is:
MyProject.UnitTests → IsTestProject=true, TestingType=UnitMyProject.IntegrationTests→ IsTestProject=true, TestingType=IntegrationMyProject.E2ETests → IsTestProject=true, TestingType=E2EMyProject.FunctionalTests → IsTestProject=true, TestingType=FunctionalMyProject.ContractTests → IsTestProject=true, TestingType=ContractThe detected test type becomes the baseline category for the project. Additional categories can still be added when they improve discoverability or filtering.
Detection itself is a pattern, not a lookup: the project name is matched against
^.*\.([\w|_]\w*)Tests?$ and the captured word becomes TestingType. So MyProject.GoldenTests is
detected exactly as readily as MyProject.UnitTests, and gets TestingType=Golden and
Category=Golden.
The name needs a target in front of it. The pattern requires the dot, so a project named plainly
UnitTestsmatches nothing: it is not detected as a test project at all, and gets no runner, no coverage and no category. Name it<Target>.UnitTests.
What the SDK does ship as an explicit list is the TestType item group, and it governs
InternalsVisibleTo generation rather than detection — a non-test project grants
access to {AssemblyName}.{TestType}Tests for each name on it. A suffix outside that list still builds,
still runs and still gets its category; it just cannot reach the target project’s internal members.
The full set is listed in Project Type Detection.
Shared testing projects
Section titled “Shared testing projects”Projects named SharedTestingFramework, SharedTestingInfrastructure, SharedTestingInfra,
SharedTestingUtilities, SharedTestingUtils, SharedTestingLibrary, SharedTestingLib, or
SharedTestingHelpers are treated as shared testing helpers — they get test package references but
not the test runner or coverage settings.
See Project Type Detection for how these names are classified, and Assembly Name Generation for how the identities are derived.