How to automate GUI tests for macOS '.dmg' installer images

If you have used Squish before, you probably already test your application thoroughly. However for a new user, there are a few steps to take until they even run the application in the first place: they need to install it. That procedure is part of the first impression you make, so it would be nice to get it right, wouldn't it? Let's take a look!

Just in case you are (also) on Windows, we outlined a procedure for MSI installers in an earlier blog post.

Since we are on macOS, applications are packaged inside disk image (.dmg) files. They usually contain either the app bundle itself or an installer package (.pkg) that performs additional setup besides extracting an app bundle to /Applications. The easiest way to get to grips with the contents of a disk image (from Squish) is to open it via the command line. But before we get our hands dirty let's start by looking at the general idea in pseudo code:
[code language="python" title="The general idea how to access the installer image"]openDmg("my_application_installer.dmg")
registerInstallerAsAUT("application_installer")
startApplication("application_installer")
closeDmg()[/code]
As you can see, there are three functions. You might recognize the next-to-last one, it is the built-in Squish function to start an application under test. The others are helpers we are going to implement in the next paragraphs to access the dmg installer image.

Opening the DMG Installer Image

Let's start with openDmg() so that we can see what's inside the image...
[code language="python" title="Mounting a dmg image"]def openDmg():
test.startSection("Open/mount .dmg file")
args = ["open", os.path.join(dmg_file_path, dmg_file_name)]
test.log("Executing: %s" % args)

try:
output = subprocess.check_output(args)
except subprocess.CalledProcessError:
test.fatal("Opening/mounting failed:")
test.fatal("File: %s" % os.path.join(dmg_file_path, dmg_file_name))
test.fatal("Output: %s" % output)
return False
finally:
test.endSection()
return True[/code]
This snippet essentially just executes the command open ~/path/to/Application.dmg and makes use of macOS's automatic dmg mounting mechanism. (The open command performs the same action as clicking an item in a Finder window would.) You'll notice we use this code pattern several times to execute the commands we need.

Registering the Installer as a Squish AUT

The disk image is mounted and macOS should have opened a window as if we had clicked it manually. The next step is to execute the installer so that we can record and replay actions on it as usual. For that to work the installer has to be registered with Squish as a separate application under test (AUT) - which boils down to another command line call:
[code language="python" title="Registering the installer as AUT"]def registerInstallerAsAUT():
test.startSection("Register installer as AUT")
args = [os.path.join(os.getenv("SQUISH_PREFIX"), "bin", "squishserver"), "--config", "addAUT", installer_file_name, installer_file_path]
test.log("Executing: %s" % args)

try:
output = subprocess.check_output(args)
except subprocess.CalledProcessError:
test.fatal("Registering installer as AUT failed:")
test.fatal("Output: %s" % output)
return False
finally:
test.endSection()
return True[/code]
The command line this time: $SQUISH_PREFIX/bin/squishserver --config addAUT your_installer_name /Volumes/Disk_Image/. As you can see: no magic involved. We have completed the two helper functions from the pseudo code we started out with. Let's get them to do something helpful!

Putting Everything Together

If we put everything together it could look like this:
[code language="python" highlight="16" title="The main() function"]def main():
if not openDmg():
return
if not registerInstallerAsAUT():
return

# Start running the installer as the AUT from here
test.startSection("Start installer as AUT")
try:
startApplication('"%s"' % installer_file_name)
finally:
test.endSection()

# Set breakpoint on the next line, execute to it, start
# recording a snippet (Run > Record Snippet):
snooze(1)[/code]
You can now teach Squish to complete your installer by recording the necessary steps. At the snooze(1) in the last line, the installer is up and running. Place a breakpoint there and start recording!

Concerning cleaning up after ourselves, you might remember the closeDmg() function in the pseudo code far above. That one just calls umount /Volumes/Your_Image to eject the image after the installer has terminated. A working implementation as well as all the source code from this post can be found in the Knowledge Base article that covers this topic as well.

If You Have an App Bundle

If the AUT just consists of an app bundle (Application.app) as outlined in the beginning, we could simply copy it to a temporary location, register it with Squish (like we did with the installer above) and run it right away.

If you have a .pkg based installer

.pkg files are not executable, and they are handled by a system application called Installer.app. Hooking this application via Squish is not currently possible, so Squish can not be used to automate the installation of apps that are packaged in this way.

Comments

    The Qt Company acquired froglogic GmbH in order to bring the functionality of their market-leading automated testing suite of tools to our comprehensive quality assurance offering.