Install
$ agentstack add skill-nice3point-revit-skills-revit-testing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Revit Testing
Every Revit API call must run on the single thread that initialized Revit. Nice3point.TUnit.Revit marshals each test and hook onto that thread through the RevitThreadExecutor; the test body reads like ordinary API code. It builds on TUnit and Microsoft.Testing.Platform; running the tests needs a matching licensed Revit installation.
It applies to a project scaffolded from the revit-tunit template — with the project structure and the assembly-level RevitThreadExecutor already configured — and covers writing tests inside it, not setting the project up.
When to use
- Writing or reviewing a test for your own logic or helpers that call the Revit API.
- Asserting that an operation produced the expected model, file, or value.
When not to use
- Providing the document, service, or parameterized cases a test consumes — use
revit-test-fixtures. - The code under test never touches the Revit API — write a plain TUnit test with no
RevitApiTestbase and no executor.
Workflow
Step 1: Write a test on the Revit thread
Inherit RevitApiTest; it exposes the shared Application. TUnit constructs a new instance of the test class for every test; instance fields and properties are always isolated between tests — use them freely for per-test state. Structure the body as distinct Arrange, Act, and Assert blocks, and assert the observable result, not framework plumbing.
public sealed class BoundingBoxExtensionsTests : RevitApiTest
{
[Test]
public async Task Union_NonOverlappingBoxes_EnclosesBothExtents()
{
// Arrange
var first = new BoundingBoxXYZ { Min = new XYZ(0, 0, 0), Max = new XYZ(1, 1, 1) };
var second = new BoundingBoxXYZ { Min = new XYZ(2, 2, 2), Max = new XYZ(3, 3, 3) };
// Act
var union = first.Union(second);
// Assert
using (Assert.Multiple())
{
await Assert.That(union.Min.IsAlmostEqualTo(XYZ.Zero)).IsTrue();
await Assert.That(union.Max.IsAlmostEqualTo(new XYZ(3, 3, 3))).IsTrue();
}
}
}
Assert.Multiple() groups related checks so one failure does not hide the rest. TUnit assertions are awaited: IsEqualTo(...).Within(tol) for doubles, IsTrue(), IsNotEmpty(), .All().Satisfy(...) for collections, and .Throws() for failures.
Step 2: Keep every Revit-touching member on the Revit thread
The assembly-level [assembly: TestExecutor] (in TestsConfiguration.cs) runs every test on Revit's thread; individual tests need no attribute.
- A
[Before]/[After]hook that calls the Revit API carries[HookExecutor]. - A test that must run off the Revit thread overrides with its own
[TestExecutor]. - Load Revit API types lazily — a field initializer runs at construction, before Revit is injected:
// BAD — runs before Revit exists
private readonly ElementId _levelId = new ElementId(BuiltInCategory.OST_Levels);
// GOOD — resolved on first use, on the Revit thread
private ElementId LevelId => field ??= new ElementId(BuiltInCategory.OST_Levels);
Discovery happens before Revit is injected and off its thread: TUnit constructs the test class, evaluates every data source, and resolves every dependency-injection service at discovery. No constructor, field initializer, data-source member, or injected service may touch the Revit API at construction — defer that work to the test body or a [Before] hook that runs on the Revit thread.
Step 3: Parameterize and supply fixtures
Feed a small fixed set of primitive cases inline with [Arguments]; the test body builds the Revit objects. [Arguments], method data sources, and custom data sources are basic TUnit features; this skill adds the Revit-thread constraints. For data-source choices beyond the Revit fixtures below, read TUnit's Method Data Sources source.
[Test]
[Arguments(3, 4, 5)]
[Arguments(0, 3, 4)]
public async Task NewXyz_Distance_MatchesLength(double x, double y, double z)
{
// Arrange
var expected = Math.Sqrt(x * x + y * y + z * z);
// Act
var point = Application.Create.NewXYZ(x, y, z);
// Assert
await Assert.That(point.DistanceTo(XYZ.Zero)).IsEqualTo(expected).Within(1e-6);
}
A data source runs during TUnit discovery, off the Revit thread; it yields plain inputs (numbers, strings, file paths) and never a Revit object. For a seeded model, an opened sample file, an injected service, or the same test across many file kinds, use revit-test-fixtures — it routes each situation to the right fixture and data source.
Step 4: Run against a matching Revit install
TUnit runs on Microsoft.Testing.Platform, and the configuration carries the target Revit version.
dotnet test -c Release.RNN
RNN is the target Revit-year configuration, for example Release.R26. Use dotnet run -c Release.RNN for simpler command-line flag passing. A licensed Revit matching the selected configuration must be installed, because the tests run against a real Revit process.
Validation
- [ ] Tests inherit
RevitApiTestand assert observable model behavior, not framework plumbing. - [ ] Every Revit-touching hook carries
[HookExecutor]. - [ ] Data sources and inline arguments carry only primitives; Revit objects are built in the test body.
- [ ] Tests do not reference
RevitAPIUItypes that require a UI session. - [ ] The selected
Release.RNNconfiguration matches the installed Revit runtime.
Common Pitfalls
| Pitfall | Correct approach | |--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| | Revit API called with no executor (thread error) | Keep the assembly-level TestExecutor; add [TestExecutor] only to override it. | | A Revit-touching [Before]/[After] hook without a hook executor | Add [HookExecutor]. | | A data source that returns a Revit object | Data sources run off the Revit thread at discovery; return primitives or paths and build Revit objects in the body. | | A field initializer that loads a Revit API type | Field initializers run before Revit is injected; move the value into a lazy field ??= … property. | | A test references a RevitAPIUI type that requires a UI session | Reference only RevitAPI in tests; RevitAPIUI needs a UI session the test host lacks. | | Asserting framework plumbing | Assert the resulting model, file, or value. | | RevitApiTest not found | The Nice3point.TUnit.Revit package is not referenced. |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Nice3point
- Source: Nice3point/revit-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.