Creating Unit Tests for AutoCAD Custom Commands
(ObjectARX/CRX) with AcCoreConsole — with Blue Code
Formatting
This edition highlights all code blocks in blue for improved readability.
Sample: C++ ObjectARX/CRX Command Wrapper (Skeleton)
// core_logic.h
struct Placement { double x, y; };
std::vector<Placement> ComputePlacements(const std::vector<int>& partIds);
// [Link]
#include "core_logic.h"
#include "aced.h"
#include "dbents.h"
#include <vector>
static void myCommand()
{
std::vector<int> parts = {101, 102, 103};
auto placements = ComputePlacements(parts);
AcDbBlockTable* pBT; AcDbBlockTableRecord* pBTR;
acdbHostApplicationServices()->workingDatabase()->getSymbolTable(pBT, AcDb::kForRead);
pBT->getAt(ACDB_MODEL_SPACE, pBTR, AcDb::kForWrite); pBT->close();
for (auto& pl : placements) {
AcDbCircle* c = new AcDbCircle(AcGePoint3d(pl.x, pl.y, 0.0), AcGeVector3d::kZAxis, 10.0);
AcDbObjectId id; pBTR->appendAcDbEntity(id, c); c->close();
}
pBTR->close();
}
extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
if (msg == AcRx::kInitAppMsg) {
acrxUnlockApplication(pkt); acrxRegisterAppMDIAware(pkt);
acedRegCmds->addCommand("MY_CMDS","MYCOMMAND","MYCOMMAND",ACRX_CMD_DEFAULT,&myCommand);
}
else if (msg == AcRx::kUnloadAppMsg) {
acedRegCmds->removeGroup("MY_CMDS");
}
return AcRx::kRetOK;
}
Sample: Native Unit Test of Pure C++ Logic (GoogleTest)
#include <gtest/gtest.h>
#include "core_logic.h"
TEST(ComputePlacementsTests, ReturnsThreePlacements)
{
std::vector<int> parts{101,102,103};
auto out = ComputePlacements(parts);
ASSERT_EQ(3u, [Link]());
EXPECT_DOUBLE_EQ(0.0, out[0].x);
}
Run the Command Headlessly with AcCoreConsole (.scr + cmd)
;; tests/scripts/[Link]
(arxload "D:/cad/[Link]")
MYCOMMAND
_.QSAVE
_.QUIT
"C:\Program Files\Autodesk\AutoCAD 2026\[Link]" ^
/i "D:\cad\tests\input\[Link]" ^
/s "D:\cad\tests\scripts\[Link]" ^
/l en-US ^
/isolate
Validation: AutoLISP Checker (prints verifiable output)
;; tests/scripts/[Link]
(defun c:CHECK ()
(princ (strcat "\nLAYER_EXISTS=" (if (tblsearch "LAYER" "ResultLayer") "YES" "NO")))
(princ (strcat "\nBLOCK_COUNT=" (itoa (length (ssget "_X" '((2 . "ResultBlock")))))))
(princ))
;; tests/scripts/[Link]
(load "D:/cad/tests/scripts/[Link]")
CHECK
_.QUIT
CI Assertion (PowerShell)
$acc = "C:\Program Files\Autodesk\AutoCAD 2026\[Link]"
& $acc /i "D:\cad\tests\input\[Link]" /s "D:\cad\tests\scripts\[Link]" /isolate |
Tee-Object -FilePath "D:\cad\tests\logs\[Link]"
$log = Get-Content "D:\cad\tests\logs\[Link]" -Raw
if ($log -match "LAYER_EXISTS=YES" -and $log -match "BLOCK_COUNT=3") { exit 0 } else { exit 1 }
Optional: NUnitLite Test Harness Inside Core Console (C#)
using [Link];
using [Link];
using [Link]; using NUnitLite;
[TestFixture]
public class DwgTests {
[Test]
public void LayerExists() {
var db = [Link];
using (var tr = [Link]()) {
var lt = (LayerTable)[Link]([Link], [Link]);
[Link]([Link]("ResultLayer"), "ResultLayer missing");
[Link]();
}
}
}
public class Program {
[CommandMethod("RUNCADTESTS")]
public static void Run() => new AutoRun().Execute(new string[]{});
}
DXF Comparison (Python + ezdxf) — Example
import ezdxf
import sys
def snapshot(doc):
layers = set([Link]())
blocks = set([[Link] for b in [Link] if not [Link]("*")])
return {"layers": layers, "blocks": blocks}
def main(a, b):
d1 = [Link](a); d2 = [Link](b)
s1, s2 = snapshot(d1), snapshot(d2)
print("Layers only in A:", sorted(s1["layers"] - s2["layers"]))
print("Layers only in B:", sorted(s2["layers"] - s1["layers"]))
print("Blocks only in A:", sorted(s1["blocks"] - s2["blocks"]))
print("Blocks only in B:", sorted(s2["blocks"] - s1["blocks"]))
if __name__ == "__main__":
main([Link][1], [Link][2])