Cat On A Spreadsheet
As your VBA codebase grows, manual testing and ad-hoc message boxes stop cutting it. Bugs slip through, refactors become risky, and teams lose confidence in making changes. A simple, structured test harness gives you automated verification of behavior, fast feedback during development, and a safer path to refactoring.
A production-ready VBA test harness doesn’t need to be complicated. It should be small, predictable, and easy for any developer on your team to use. The core ingredients are:
A set of lightweight assertion helpers (AssertEqual, AssertTrue, etc.).
A test runner that finds and executes test procedures.
A consistent way to report results (worksheet, Immediate window, or a CSV).
Optional setup/teardown hooks for tests that need controlled state.
Below I walk through a compact, reusable harness pattern you can drop into any workbook.
Keep these principles in mind when designing the harness:
Tests should be independent: each test must set up its own prerequisites.
Keep assertions descriptive: when something fails, the message should tell you what and where.
Make test discovery simple: name tests consistently so the runner can auto-detect them.
Prefer deterministic behavior: avoid randomness in tests unless seeded.
These lead to quick, trustworthy feedback and easy adoption by teammates.
Start with a small module of assertion routines. They are the primitives tests use to declare expected behavior. The examples below write results to a simple TestResults sheet, but you can adapt output to CSV or the Immediate window.
A simple discovery rule is to name test procedures starting with Test_. The runner reflects over all standard modules and executes public Subs that match that prefix. Keep the runner small so it’s easy to understand and extend.
Note: VBA’s programmatic code inspection (VBProject access) is subject to a Trust Center setting. If you prefer not to require that setting, keep the harness discovery simple: maintain a small TestList function that returns an array of test procedure names — it’s explicit and easy to control.
Create a module for tests and list them. Tests should be tiny and focused.
And a tiny library under test:
For tests that need a controlled environment (files, temporary sheets, mocked ranges), provide optional Setup and Teardown conventions. For example, if a test sub is named Tests_TestX, allow a Tests_TestX_Setup and Tests_TestX_Teardown pair that the runner calls automatically before and after the test. That keeps tests isolated and reproducible.
The harness above writes the pass/fail rows to a TestResults worksheet. That sheet is easy to parse programmatically or export as CSV for CI systems. For continuous integration you can:
Save the workbook headless (with Application.DisplayAlerts = False) after tests and export TestResults to CSV programmatically.
Have an external scheduler or script open Excel, run RunTests, and then collect the TestResults.csv.
If your team uses a third-party tool (Jenkins, Azure DevOps), the exported CSV becomes the test artifact.
Keep tests fast: make unit tests run in milliseconds. If a test touches I/O, mark it as integration and run less frequently.
Group tests by feature module for targeted runs.
Use deterministic seeding for random functions.
Make failure output actionable: include input, expected, actual, and any relevant state.
Consider using an external test runner like Rubberduck if you want rich unit test tooling — the harness above is intentionally minimal and dependency-free.
A small, disciplined test harness transforms VBA projects from fragile spreadsheets into reliable automation. It reduces fear of refactoring, speeds onboarding for new developers, and provides an auditable trail of verification. Start small: write tests for the core functions you change most often, and expand coverage iteratively. Over time, that investment pays back many times over in stability and developer confidence.
Cat On A Spreadsheet