RevitDevTool.TestAdapter (NuGet) is the only supported test integration. NUnit is
the default engine, TUnit is opt-in. Product: docs/product/host-testing.md ·
docs/product/tunit-host-testing.md. Structure: docs/architecture/Testing/README.md.
Run/author tests: .agents/skills/revit-test/SKILL.md.
test csproj (HostName, HostVersion, NUnit|TUnit)
-> package targets: Reference the selected DevTools.{NUnit|TUnit}.MTP.dll
+ Abstractions, write discovery-refs.txt and [AssemblyName].testconfig.json
-> generated MTP entry point calls adapter hook + selected sibling hook
-> discovery: local ExploreTests / TUnit catalog in the testhost, no host process
-> run: TestRunner.exe locates or launches the host, then testing/hello|run|cancel
Adapter writes devtools.frameworkId to testconfig.json. Missing
frameworkId on a hand-written devtools section is a build Error. Sibling
selection is MSBuild (TestingFramework → one Reference + builder hook),
not runtime plugin keys.
# 1. compile + in-repo tests (adapter unit tests still ProjectReference)
dotnet build source/DevTools.Testing.Host/DevTools.Testing.Host.csproj -c Debug
dotnet run --project tests/DevTools.TestAdapter.Tests/DevTools.TestAdapter.Tests.csproj
dotnet run --project tests/DevTools.NUnit.MTP.Tests/DevTools.NUnit.MTP.Tests.csproj
# TestRunner.Tests is MSTest.Sdk 4.4 (MTP testhost), not xunit
dotnet run --project tests/DevTools.TestRunner.Tests/DevTools.TestRunner.Tests.csproj
# 2. pack to output/nuget + drop stale global-packages extraction (default)
scripts/pack-test-adapter.ps1
# 3. package surface (restore from that nupkg + host-free discovery), net48 / net8 / net10
scripts/test-adapter-matrix.ps1
# 4. live (host running; root global.json is MTP)
dotnet test --project samples/DevTools.NUnit.SampleTests/DevTools.NUnit.SampleTests.csproj -c Debug.Autodesk.2026 --filter Arithmetic_runs_inside_hostHost DLL changes: scripts/build-host.ps1 -Year <year>. Runner:
dotnet publish source/DevTools.TestRunner -c Release. Adapter nupkg:
scripts/pack-test-adapter.ps1 (not scripts/pack.ps1). The pack script
deletes %USERPROFILE%\.nuget\packages\revitdevtool.testadapter\<version>
so the next sample restore cannot keep a previous extraction of the packed version.
- Consumer properties:
HostName,HostVersion,ForceLaunch,PerTestTimeout,LaunchTimeout,TestingFramework(nunitdefault,tunitopt-in).UseRevit/UseAutoCadare this repo's sample compile flags, not package settings. - Do not set
RuntimeIdentifieron net8 / net10 test projects. The package setsAppendRuntimeIdentifierToOutputPath=false. A RID on net8 (Revit 2025) nests testhost output underwin-x64. Samples setwin-x64only whenTargetFrameworkstarts withnet4. net48+ TUnit needs noPolyfill. The package defaultsEnableTUnitPolyfills=falseand compilesbuild/netfx/ModuleInitializerAttribute.csinto the project, skipping it when aPolyfillreference or anotherModuleInitializerAttribute.csis already there. Opt out withNetFxModuleInitializer=falseonly when that skip misses the existing type (seedocs/product/tunit-host-testing.md).- The packaged
build/*.props|targetsmust not read$(Configuration), repo paths, orProjectReference. In-repo samples restoreRevitDevTool.TestAdapterfromoutput/nuget(same nupkg an end user gets). There is no checkout-onlyLocal.targetspath. --filteris the method-name option (NUnit<name re="1">regex).--filter-uidis the TestNode uid (ITest.FullName;TestName/SetNameisClass.Method("DisplayName")).--list-teststext prints DisplayName — never paste a text line as--filter-uid. TestRunner does not discover tests.- MTP samples:
dotnet test --project samples/DevTools.*.SampleTests/…from the repo root (rootglobal.jsonis MTP).samples/ricaun.NUnit.SampleTestsoverrides to VSTest —cdinto that folder. In-repotests/:dotnet run --project tests/<proj>/<proj>.csprojstill works;dotnet test --project tests/<proj>/<proj>.csprojfrom root is now MTP too. Never VSTest--filter FullyQualifiedName~on MTP projects. - Use an Autodesk configuration (
Debug.Autodesk.2025, …). PlainDebugdoes not setRevitVersion/TargetFramework, so the sample does not build and Test Explorer falls back to a source tree that is not MTP discovery. - Autodesk configs flatten host obj/bin; MTP keeps
AppendTargetFrameworkToOutputPath=trueso its three TFMs never share a folder (CS2012 / MSB3713). Do not collapseTargetFrameworkson packable projects.
- Discovery must stay host-free: Test Explorer refresh and
--list-testsmust not start a host, and must not readtestconfig.jsonhost options.ForceLaunch=falsestill starts a matching-version host on run when none is open. - "Test discovery aborted: 0 Tests found" = the testhost died in static init or Discover.
Usual cause is a timestamp-stale
DevTools.*.MTP.dllbeside the test exe (TypeLoadExceptionon anITestDiscoverermember). Rebuild the test project, not only the host year; the sibling copy runs withSkipUnchangedFiles=false. - Do not use
NUnit.Enginein the host, and never addNUnit3TestAdapterto a host-test project.samples/ricaun.NUnit.SampleTestsis the third-party VSTest comparison sample over the sameHostSmokeTests; its explorer tree is not a DevTools discovery bug.Intentional_failure_for_demois an expectedAssert.Fail. - net48 "could not be discovered" = testhost failed to bind
Unsafe6.0. That BCL comes from the adapter'sMicrosoft.Testing.Platform.MSBuildgraph plusAutoGenerateBindingRedirects; the nupkg ships no loose 3rd-party DLLs. - net48 has no load context: if the host already loaded an assembly with the same identity, in-host tests bind that copy, not the generation snapshot. Restart the host after deploying, or use net8+.
- Stream-loaded assemblies have an empty
Assembly.Location. Tests that locate assets use NUnitTestContext.WorkDirectory(the generation shadow). - Live
testing/runis marshaled throughIHostContextExecutor(NUnitRunOnMainThread). WPFDispatcher.Invokeis not a Revit API context. - Do not add a Host
TraceListenerorILoggerdump ofCaseResult.Output; Trace/Debug already fan out and Console is write-through at case finish (0017). - TUnit in-host
MissingMethodExceptiononClientInfoService: MTP 2.4.0 needsIClientInfo+IClientCapabilities; the Runtime registersTUnitEngineClientInfoand ships in the installer, so redeploy the host (not the nupkg). - Visual Studio Debug attaches the testhost, then the Runner EnvDTE-attaches that VS
instance to the host (0025).
Stop Debugging during launch kills only a host this run spawned. Rider / C# Dev Kit:
attach the host PID, then Run. Runner host year is
--host-version.