ASUnit is a unit testing framework for AppleScript derived from the
original codebase1. For a detailed description of the
architecture of the original ASUnit framework, read the old
manual; some advanced features of ASUnit (such as custom
TestCase
and Visitor
objects) are still described only in that document.
ASUnit's API is now thoroughly commented using HeaderDoc.
Visit Releases to download the current release.
Alternatively, clone the repository from GitHub:
git clone https://github.com/lifepillar/ASUnit.git
If for some reason you want to download a specific version of ASUnit from the terminal or with a shell script, you may adapt the following commands:
VERSION="1.2.5"
TARBALL="${VERSION}.tar.gz"
ASUNIT_BASE_URL="https://github.com/lifepillar/ASUnit/"
TARBALL_URL="${ASUNIT_BASE_URL}/archive/refs/tags/${TARBALL}"
cd ~/Downloads
curl -O "${TARBALL_URL}"
tar zxvf "${TARBALL}"
Or, if you prefer a Zip archive:
VERSION="1.2.5"
ZIPFILE="${VERSION}.zip"
ASUNIT_BASE_URL="https://github.com/lifepillar/ASUnit/"
ZIPFILE_URL="${ASUNIT_BASE_URL}/archive/refs/tags/${ZIPFILE}"
cd ~/Downloads
curl -O "${ZIPFILE_URL}"
unzip "${ZIPFILE}"
Change VERSION
above to match the version you want to download.
Once you have downloaded ASUnit, to build and install it you may proceed in two different ways.
- If you use
AppleScript
2.4 (OS X 10.10 "Yosemite") or later and have installed ASMake, you may just write:
cd ASUnit
./asmake install
- Otherwise, you can install it manually with the following commands:
cd ASUnit
osacompile -o ASUnit.scptd -x ASUnit.applescript
mkdir -p "${HOME}/Library/Script Libraries/com.lifepillar"
mv ASUnit.scptd "${HOME}/Library/Script Libraries/com.lifepillar"
In either case, the file ASUnit.scptd
will be installed in
~/Library/Script Libraries/com.lifepillar
.
To use ASUnit, add one of the two properties below to your script in order to import the library.
If you have AppleScript
2.3 (OS X 10.9 "Mavericks") or later, use this:
property parent : script "com.lifepillar/ASUnit"
If you have an older, pre-Mavericks system, use this instead:
property parent : ¬
load script (((path to library folder from user domain) as text) ¬
& "Script Libraries:com.lifepillar:ASUnit.scptd") as alias
Your test script must define a suite
property and pass it to ASUnit's
autorun()
handler:
property suite : makeTestSuite("A description for my tests")
autorun(suite)
You may run the test script in various ways: inside Script Editor, from the
command-line using osascript
, or in other environments (Script
Debugger, AppleScriptObjC Explorer).
When you have several test files, you may run them all at once using a test
loader (there is no need to compile them in advance). See Test Loader.applescript
in the examples folder.
By default, if you run the tests in Script Editor, the
output is written to a new Script Editor document; if you run the tests
in the Terminal, the output is sent to stdout; otherwise,
the output is sent to the current system logger through log
statements;
access them via Console.
You may, however, change this by setting the suite's loggers
property. The
value of this property must be a list of loggers (you may send the output to
more than one destination). Currently, ASUnit defines three loggers:
AppleScriptEditorLogger
: sends colored output to a Script Editor window;StdoutLogger
: sends colored output to stdout.ConsoleLogger
: prints the output usinglog
statements (most portable logger).
Defining custom loggers should be fairly easy: you simply need to define
a script that inherits from TestLogger
and override the print…()
handlers
to generate the output you want. A more advanced alternative consists in
subclassing Visitor
: you may find an example in the old
manual.
A test template is provided in the templates folder. See the examples folder for complete examples. The general structure of a test script is as follows:
script |One test set|
property parent : TestSet(me)
on setUp()
-- Code executed before each unit test
end
on tearDown()
-- Code executed after each unit test
end
script |a test|
property parent : UnitTest(me)
assert(1 + 1 = 2, "one plus one should be two")
assertEqual(2, 1 + 1)
refute(3 = 1 + 1, "1 + 1 should not be 3")
end script
script |another test|
property parent : UnitTest(me)
-- More assertions…
end
end script
script |Another test set|
property parent : TestSet(me)
-- More tests…
end
Each unit test is a script that inherits from UnitTest(me)
. Inside such
scripts, you may make a number of assertions.
Below, “iff” stands for “if and only if”.
skip(msg)
: skips the current test.fail(msg)
: makes the test unconditionally fail.ok(expr)
: succeeds iff the booleanexpr
evaluates to true.notOk(expr)
: succeeds iffexpr
evaluates to false.assert(expr, msg)
orshould(expr, msg)
: succeeds iffexpr
is true.refute(expr, msg)
orshouldnt(expr, msg)
: succeeds iffexpr
is false.shouldRaise(num, object, msg)
: succeeds iffobject
raises exceptionnum
when executed. Theobject
can be a script object or a handler without parameters.shouldNotRaise(num, object, msg)
: succeeds iffobject
does not raise exceptionnum
when executed. Theobject
can be a script object or a handler without parameters.assertEqual(expr, value)
orshouldEqual(expr, value)
: succeeds iffexpr
=value
.refuteEqual(expr, value)
orshouldNotEqual(expr, value)
: succeeds iffexpr
≠value
.assertMissing(expr)
: a synonym forassertEqual(missing value, expr)
.assertObjCReference(expr)
: succeeds iffexpr
is a reference to a Cocoa object.refuteObjCReference(expr)
: succeeds iffexpr
is not a reference to a Cocoa object.refuteMissing(expr)
: a synonym forassertNotEqual(missing value, expr)
.assertNull(expr)
: a synonym forassertEqual(null, expr)
.refuteNull(expr)
: a synonym forassertNotEqual(null, expr)
.assertEqualAbsError(e1, e2, delta)
: succeeds iff|e1-e2| <= delta
.assertEqualRelError(e1, e2, eps)
: succeeds iff|e1-e2| <= min(|e1|,|e2|) * eps
.assertReference(x)
orshouldBeReference(x)
: succeeds iffx
is a reference.assertNotReference(x)
orshouldNotBeReference(x)
: succeeds iffx
is not a reference.assertInstanceOf(aClass, expr)
: succeeds iff the class ofexpr
is equal toaClass
.refuteInstanceOf(aClass, expr)
: succeeds iff the class ofexpr
is notaClass
.assertKindOf(aClass, expr)
: succeeds iffexpr
or any of its ancestors belongs toaClass
.refuteKindOf(aClass, expr)
: succeeds iff neitherexpr
nor any of its ancestors belong toaClass
.assertInheritsFrom(a, b)
: succeeds iffb
(directly or indirectly) inherits froma
.refuteInheritsFrom(a, b)
: succeeds iffb
does not inherit froma
.
Some of the assertions take a textual message as an argument (msg
parameter),
which is printed when the assertion fails.
A clarification is in order for the last three types of assertions. Consider the following two scripts:
script A
property class : "Father"
end script
script B
property parent : A
property class : "Child"
end script
Then, these assertions must succeed:
assertInstanceOf("Father", A)
assertInstanceOf("Child", B)
refuteInstanceOf("Father", B)
assertKindOf("Father", B)
refuteInstanceOf(script, A)
assertKindOf(script, A)
assertInheritsFrom(A, B)
refuteInheritsFrom(B, A)
Related unit tests can be grouped together into a script that must inherit from
TestSet(me)
. One advantage of grouping tests is that you may define setUp()
and tearDown()
operations that are automatically executed before and after
each unit test, respectively. Such handlers can be used for initialization of
data structures and clean up operations, and help ensure that each unit test is
not affected by the behavior of the others.
Note that the names of the scripts are used in the output. For this reason, you
may want to use short sentences enclosed between vertical bars as script names,
as it was done in the example above. Alternatively, you may define the name
property of the script explicitly.
Copyright © 2013 Lifepillar, 2006 Nir Soffer. All rights reserved.
This software is licensed under the GNU GPL-2.0 License, see COPYING for details.
Footnotes
-
The original framework codebase was written by Nir Soffer. ↩