consedge.

How to test a Revit add-in: logic tests, in-Revit tests and UI automation

Zakhar Vityk, CEO, Consedge Tech

Most Revit add-ins are tested the same way: a developer clicks through the commands before a release. That works until the add-in has twenty commands, supports four Revit versions, and the runtime changes in the middle of a version's life.

This guide describes a test strategy that fits Revit's constraints. It covers what to test outside Revit, what must run inside it, how to automate the interface, and how to check results that are geometry instead of numbers.

Why Revit add-ins are hard to test

  • The API lives inside Revit. You cannot create a document or an element in a plain test process. There is no official mock.
  • One thread, one context. API calls are valid only at certain moments, and changes need transactions.
  • Results are visual. The output is often a view, a sheet or a solid, not a return value.
  • Revit is slow to start and needs a licence. That shapes what a build server can do.
  • Several versions at once. A test that passes in one Revit version says little about the next.

The test layers

LayerRuns whereWhat it coversSpeed
Logic testsPlain .NET, no RevitRules, naming, calculations, parsing, settingsMilliseconds
In-Revit testsInside the Revit processCollectors, transactions, element creation, per-version API behaviourSeconds to minutes
UI testsAgainst a running RevitDialogs, ribbon, modeless windowsMinutes
Result checksAfter a commandGeometry, drawings and exports against referencesVaries

The goal is to push as much as possible into the first row.

Layer 1: make the logic testable

The single most useful change is architectural. Separate deciding from doing.

Read what you need from Revit into plain objects. Compute a plan from them: what to create, rename or change. Apply the plan in a thin layer that talks to the API. The middle step is ordinary code, and ordinary tests cover it.

// Pure logic: runs in any test project, no Revit needed
public static string BuildViewName(RoomInfo room, NamingRule rule) =>
    rule.Pattern
        .Replace("{number}", room.Number)
        .Replace("{name}", room.Name.ToUpperInvariant());

[Test]
public void View_name_follows_the_rule() =>
    Assert.That(
        Naming.BuildViewName(new RoomInfo("101", "Lobby"), new NamingRule("{number} - {name}")),
        Is.EqualTo("101 - LOBBY"));

These tests run on every commit, on any machine, in a second.

Layer 2: tests inside Revit

Some behaviour exists only in Revit: what a collector returns, whether a transaction commits, how an API call behaves in each version. For that you need a runner that executes NUnit tests in the Revit process.

Two options are worth knowing. Revit.TestRunner runs tests inside Revit and is what we use. ricaun.RevitTest starts Revit from Visual Studio or dotnet test and supports several Revit versions.

Habits that keep these tests reliable:

  • Each test opens a known reference model or creates a document from a template.
  • Each test rolls back what it changed. No test depends on another.
  • Assertions read the model through the API: counts, parameter values, bounding boxes.
  • The same tests run against every supported Revit version.

Layer 3: interface automation

FlaUI drives Windows interfaces the way a user does. For an add-in, it opens a command, fills in the dialog, clicks OK and waits.

Two development habits make this practical. Give every control a stable automation ID. And add a test mode that answers Revit's own task dialogs in code, so a warning does not block the run.

Keep UI tests few. They are slow and break when layouts change. Cover the flows a user cannot work without.

Layer 4: check the result, not the absence of errors

A command that finishes without an exception can still produce the wrong thing.

  • Data. Export the affected parameters or a schedule and compare with a stored reference file.
  • Geometry. Export the result as a mesh and compare it with a reference numerically, using a distance measure and a tolerance. Libraries such as trimesh and Open3D do the comparison. Two results can have the same element count and different shapes.
  • Drawings. Compare view names, counts, scales and viewport positions. For printed output, compare images with a tolerance.

Reference models

The models matter as much as the tools. Keep three:

  1. A small, clean file for fast tests.
  2. A large workshared file with links, for performance and worksharing behaviour.
  3. A deliberately messy file that collects every odd case found so far: in-place families, groups, design options, phases, pinned elements, parts.

Every bug found in the field adds an element to the third file and a test that uses it. Never use client models for this without permission.

CI realities

  • Logic tests run on every commit, on any build agent.
  • In-Revit and UI tests need a Windows machine with Revit installed and licensed. A self-hosted runner and a nightly schedule are the usual answer.
  • Run the matrix: one pass per supported Revit version.
  • Keep the evidence: logs, Revit journal files and a screenshot on failure.
  • Track speed. RevitBenchmark measures code inside Revit, so a slowdown shows up as a number.
Fast tests on every commit, Revit tests nightly on every version

Only the logic layer needs nothing special; the other three wait for a licensed Revit and run once per version.

Where to start if you have nothing

  1. Move your three most valuable rules into pure functions and test them.
  2. Add one in-Revit smoke test per command: it runs on the reference model and changes the expected number of elements.
  3. Test the installer on a clean machine for each Revit version.
  4. From now on, every fixed bug gets a test.

For most add-ins that is a modest amount of work, and it changes how releases feel.

Where AI fits

AI assistants write good unit tests when the logic is separated as in Layer 1. They also write plausible API calls that do not behave as expected in a given Revit version. In-Revit tests are what catch that. More in what it takes to ship an AI-built Revit plugin.

How we work

We apply this approach to our own Revit products first. The libraries mentioned here are part of our list of open-source repositories for Revit developers.

If you want to know where the gaps in your add-in are, request a Production Readiness Audit. It includes a review of what is and is not tested. See also plugin development.

FAQ

Can I unit test Revit API code without Revit?

Only the logic you separate from the API. Anything that touches a document or an element has to run inside Revit.

Which test framework works with Revit?

NUnit is the common choice, with a runner that executes the tests inside the Revit process.

Can Revit tests run in CI?

Yes, on a self-hosted Windows runner with Revit installed and licensed. Logic tests can run anywhere.

How do I test a command that creates geometry?

Export the result and compare it with a stored reference using a numeric distance measure and a tolerance.

Working on something like this?

Thirty minutes with the engineers who would build it, then a written verdict — including “don’t build this” where that is the answer.

  • First call free, no obligation
  • Straight answers
  • Fixed-price written verdict
  • Senior engineers, not account managers
Book a Feasibility ReviewRequest a Production Readiness Audit
Plugin & Custom Software Development

Share this article