Introduction to ASUnit and AppleScript
In today's blog, I wanted to share my experience using ASUnit to write unit tests for AppleScript. I had written my own unit test library and that worked well enough, but I wanted to explore other options that could provide a more comprehensive and standardized approach to testing AppleScript code.
Setting Up ASUnit for AppleScript
To set up, I followed the following steps, which are available in the README.md of the ASUnit project.
- Clone the project:
git clone https://github.com/lifepillar/ASUnit
- Run the following commands in your terminal:
cd ASUnit
osacompile -o ASUnit.scptd -x ASUnit.applescript
mkdir -p ~/Library/'Script Libraries'/com.lifepillar
mv ASUnit.scptd ~/Library/Script\ Libraries/com.lifepillar
Setting Up Templates (Optional)
There are files you may want to make available in your template files so that you can add ASUnit codes easily via the context menu in Script Editor.
- Navigate to your templates folder in `/Library/Scripts/Script Editor Scripts` and create a new folder. I called mine ASUnit. Remember that this will be the parent menu when you trigger the context menu in Script Editor.
- Create new templates for each of the sample unit files in the templates folder of the ASUnit project. Don't worry if you need to learn how to do this; drop me a comment, and I can write about it if you're interested to learn about that.
Test Folder Structure
Once you are set up, you'll have to decide how to structure your scripts and your tests. I have test files mirrored under the test directory at the root of my project. Another difference is that my scripts are saved as .applescript instead of .scpt. That is because I've thought of .scpt as the compiled version of the script, and I wanted to keep the text version for version control purposes. To write your first unit test with ASUnit, you can start by copying the file Test Template.applescript
from the templates folder and renaming it. I named mine as plutilTest.applescript
, basically, it's the library name with the "Test" suffix. Once I figured out how to set up the object to test, I added the execution and the assertions. It is useful to have the README.md open to serve as a reference to the different assertions available.
Initial Code Changes
- Make sure the script's run handler is compliant with ASUnit; that is, it had to return a reference to the script instance. I had to make small changes here because I've used run handler to run some spot checks during development.
plutil.applescript
(* Used by ASUnit to access this script. *)
on run
tell application "System Events"
if name of (path to me) is "plutil.applescript" then
my spotCheck()
return
end if
end tell
me
end run
- Make sure the script can be compiled into .scpt if it still needs to be an.scpt file. In my case, I simply need to run a build command so that my script to test was compiled to .scpt in the Script Libraries folder. This can be easily accomplished by running a generic command that would run a shell command like:
osacompile -o ~/Library/Script\ Libraries/plutil.scpt plutil.applescript
- Update the global and script properties accordingly
---------------------------------------------------------------------------------------
property suitename : "The test suite description goes here"
property scriptName : "plutil" -- The name of the script to be tested
property plist : "~/applescript-core/test-plutil.plist"
global sutScript -- The variable holding the script to be tested
---------------------------------------------------------------------------------------
- Update the first test case called "|Loading the script|" and make sure you can make that test pass by updating the reference to the location of the test script and the script to test.
script |Load script|
property parent : TestSet(me)
script |Loading the script|
property parent : UnitTest(me)
try
tell application "Finder"
set deploymentPath to ((path to library folder from user domain) as text) & "Script Libraries:"
end tell
set sutScript to load script (deploymentPath & scriptName & ".scpt") as alias
end try
assertInstanceOf(script, sutScript)
end script
end script
- Once all these items were addressed, I went on ahead and migrated my existing test to ASUnit.
script |plutil tests set|
property parent : TestSet(me)
property sut : missing value
on setUp()
set sutLib to sutScript's new()
set sut to sutLib's new("test-plutil")
end setUp
on tearDown()
try
do shell script "plutil -remove 'key-root' " & plist
end try
end tearDown
script |Delete String Key|
property parent : UnitTest(me)
do shell script "plutil -replace 'test-string' -string 'some value' " & plist
ok(sut's deleteKey("test-string"))
try
set shellResult to do shell script "plutil -extract 'test-string' raw " & plist
fail()
end try
end script
end script
After migrating some of the tests, I am quite pleased with the result.
Conclusion: The Value of Unit Testing with ASUnit
ASUnit is a very well-written tool that greatly enhances the process of unit testing in AppleScript. Using ASUnit allowed me to write comprehensive unit tests for my AppleScript projects, ensuring that my scripts were functioning correctly and consistently. Additionally, ASUnit provided me with a separation of the tests from my actual scripts, making it easier to manage and maintain my codebase.