Skip to content
AgileLogics
8 min read

Testing and Debugging

Prerequisites: Packages and paclets

Messages

When something goes wrong, the language prints a message and usually returns a result anyway:

1/0
Power::infy: Infinite expression 1/0 encountered.
ComplexInfinity

Messages are named symbol::tag. Quiet[expr] suppresses them; Check[expr, failValue] returns failValue if any message was issued:

Check[1/0, "failed"]
Power::infy: Infinite expression 1/0 encountered.
failed

Your own messages

safeSqrt::neg = "Argument `1` should be non-negative.";
safeSqrt[x_?NonNegative] := Sqrt[x]
safeSqrt[x_] := (Message[safeSqrt::neg, x]; $Failed)
safeSqrt[16]
safeSqrt[-4]
4
safeSqrt::neg: Argument -4 should be non-negative.
$Failed

`1` in a message template is replaced by the first extra argument to Message. Returning $Failed — or a structured Failure[...] object — makes errors easy to detect with FailureQ.

Enclose and Confirm

Confirm functions stop a computation at the first problem, and Enclose catches it and returns a Failure:

ratio[a_, b_] := Enclose[
  ConfirmAssert[b != 0, "b must be nonzero"];
  a/b
]
ratio[6, 4]
FailureQ[ratio[1, 0]]
3/2
True

The failure object records the message and where it happened, which is much easier to debug than an unexpected symbolic result further down the line.

Looking inside a computation

  • Echo[expr, "label"] prints the labelled value and returns it, so you can drop it into the middle of any expression.
  • Trace[expr] lists evaluation steps; TracePrint prints them as they happen.
  • Print works anywhere; Monitor[loop, i] shows a live progress value.
  • The VS Code extension and Wolfram Workbench provide breakpoints.

Unit tests

VerificationTest[input, expected] evaluates input and checks it against expected — and that no unexpected messages appear. TestReport runs a group of tests:

report = TestReport[{
  VerificationTest[circleArea[2], 4 Pi],
  VerificationTest[squareArea[3], 9],
  VerificationTest[1/0, ComplexInfinity, {Power::infy}]
}];
report["ReportSucceeded"]
True
  • The third argument lists the messages a test expects — here the division-by-zero message.
  • Put tests in a .wlt file and run TestReport["tests.wlt"] from a script, so your continuous-integration pipeline can run them with wolframscript.
  • Version 13.3 added TestCreate and TestEvaluate for creating and running test objects separately; VerificationTest still works everywhere.
  • These tests use circleArea and squareArea from the "Packages and Paclets" lesson — load Geometry.wl first.