Skip to main content

Productivity Software So Good, It Pays For Itself: Episode 1 - TextExpander

"I told you. I wake up every day, right here, right in Punxsutawney, and it's always February 2nd, and there's nothing I can do about it."

~ Phil Connors, Groundhog Day

The life of a tester involves a lot of repetition. In even ideal environments where you can focus mainly on exploration (rather than rote, unthinking execution of endless, soul-crushing test case minutia) you will still find the need to repeat some of the same tasks many times. Some of those tasks are mechanical and serve an operational purpose (e.g. deploying a fresh environment in which to play). These you can script away to great effect (and I'll detail some of how I do that on mobile devices in a different post).

Other tasks are more for communication such as generating detailed, informative bug reports. This is typically viewed as the bread and butter of a tester's life. Whether you see it that way or not, there is value in recognizing the structure of those reports and automating away the repetitive construction of those frameworks so that you can get to the heart of the work which is in characterizing the bug itself. The frameworks are typically templatized in your bug tracking software so that the act of reporting a bug itself is reduced to merely filling out a form.

Common to the last few companies where I've worked, that form comes from Atlassian's Jira. Depending on the maturity of the project, the urgency of the work, the types of testing I'm doing, I have found myself spending up to half my day or more in Jira, repetitively reporting issues; filling in the same forms time and again. It isn't glamorous, it doesn't really pay all that well, but this service drives much of what leads to the gradual improvement of much of the software used around the world. And it can be drastically improved by TextExpander.

A former lead of mine once did the math this way (and I am paraphrasing here):
Uriah M: "Hey, Russell, got a second to talk about that TextExpander tool I asked you to download earlier this week?"
Me: "I guess so. What's up?"
Uriah M: "What sorts of framing do you supply to the bugs you write? You know, headers, repro steps, descriptions, environment, all that stuff?"
Me: "Oh the usual: description, impact, device details, environment, repro, build number, etc"
Uriah M: "How long would you estimate it takes you to write those unchanging strings out each time you enter a bug?"
Me: "Maybe 2 minutes."
Uriah M: "Okay, let's say you average 10 bugs a day. Would you agree that's 20 minutes a day of time doing that work?"
Me: "Sure."
Uriah M: "Okay, now let's say you work 5 days a week for 48 weeks a year. Does that sound about average?"
Me: "Sadly."
Uriah M: "So let's do some back-of-the-napkin math. 20 minutes per day times 5 days per week times 48 weeks per year comes out to what, 4800 minutes? What's that in hours?"
Me: "80 hours."
Uriah M: "So what if I told you there is an inexpensive piece of software that could save you two weeks of work each year? Would you use it? Would you want to work with anyone who refused to use it?"
Me: "Right. I'll go install it now."

And that's basically the sales pitch he made for TextExpander. It turns out, while I might not average 10 bugs a day, it still very easily pays for itself at my salary in well under a year's time. Here's how it works...

Say there is a long series of strings you repeatedly enter in forms without variation (or with variation that can be predictable). Now imagine if there was a tool that could record that series of strings and respond to keywords you type in to replace a simpler phrase (such as "newbug") with the pre-Jira-formatted brilliance you'd otherwise be laboring over. That's basically the short version of how TextExpander works. Each time you repeat that phrase "newbug", TextExpander get's cheaper and cheaper to where you could be basically getting 2 weeks of productivity back each year.

TextExpander calls that pre-Jira-formatted brilliance a "snippet" and you can write many of them to suit individual needs, reference snippets from inside other snippets and so on. Snippets can be dumb text replacements, execute shell commands, run Applescript (that's right, I'm a Mac-user), and other fun things. My stable of snippets runs about 6-12 deep and covers the most repeated tasks I need when entering, commenting on, or closing bugs in Jira.

My typical usage involves a "Plain Text"-type snippet which refers to several "Shell Script"-type snippets. This gives me a form that is fairly readable that has subsections which get automatically populated using a series of shell scripts to read from the output of my deployment and other scripts which I use to set up my testing. In practice that means I run one script with some convenient settings to download and install a new build potentially even launching it with a given launch configuration. If I find a bug, I don't have to spend any time looking up relevant but trivial information such as 1) which devices I'm testing on, 2) what build number I wound up with, 3) what launch configuration I used, and 4) what specific URL (signifying branch project, etc) I used. This information is relatively static but useful for precisely defining the reproducibility of a bug. The install script I use collects it, the TextExpander snippets I use read, collate, then repeat it in a pretty package saving me huge amounts of time and making my bug reports very detailed and consistent.


My "newbug" Snippet - Content: Plain Text

h2. Description

h2. Impact

h2. Devices/Notable settings
h2. Device list

h4. Developer options
* strict mode: enabled
* do not keep activities: enabled

h2. Build info
* build versionName: %snippet:BuildVersion%
* build resource URL: %snippet:BuildURL%

h2. Launch Config
* ADB launch string:
{noformat}adb -d %snippet:LaunchConfig%{noformat}

h2. Logcat

My "closebug" Snippet - Content: Plain Text

Tested on device(s):

Tested on build:
* build versionName: %snippet:BuildVersion%
* build resource URL: %snippet:BuildURL%

Tested on config:
* %snippet:LaunchConfig%
* connected via office wifi

My "DeviceTrivia" Snippet - Content: Shell Script

less /Users/russell/Documents/test-builds/main/device_trivia.txt

Where device_trivia.txt is generated by a shell script that runs whenever I use my main download/install script.

My "BuildVersion" Snippet - Content: Shell Script

less /Users/russell/Documents/test-builds/main/build_version.txt

Where build_version.txt is generated by my main download/install script.

My "BuildURL" Snippet - Content: Shell Script

less /Users/russell/Documents/test-builds/main/build_url.txt

Where build_url.txt is generated by my main download/install script.

My "LaunchConfig" Snippet - Content: Shell Script

less /Users/russell/Documents/test-builds/main/launch_config.txt

Where launch_config.txt is generated by my main download/install script.

In the case of all of these examples, along with setting up the scripts that I use to download/install/launch builds and generate device trivia, it takes maybe a day or so to get this well-oiled and rolling.  I'll talk about the scripts I use in another post but the point is, I can use these snippets with TextExpander in ways that simple copy-paste from a dummy template text file can't be used. As you can see, these snippets react to my environment and other inputs I supply when choosing how to test the apps I test. That makes these fairly portable across teams too. My hope is that you will try out TextExpander in your own work since it is so economical as to pay for itself with even moderate use. Stay tuned in the coming days/weeks when I'll write about the scripts that I use for all that automated download/install/launch/device stuff. Trust me, they really help TextExpander shine.


  1. I liked the way the process is presented and figured out. As per my knowledge, productivity comes up in various ways but the ideal way to think of is how that could be figured out and makes it end up with profitability. I believe time to be one of the major aspect to be considered and to have it managed and utilized at its best, I prefer using the cloud based time tracking software from Replicon that is again featured with the user friendly and calendar based interface which makes it an intuitive tool to work with.


Post a Comment

Popular posts from this blog

UiAutomator and Watchers: Adding Async Robustness to UI Automation

"I'm looking over your shoulder... only because I've got your back." ~ Stephen Colbert
After my recent UiAutomator review a user brought up an important question about the use of UiWatcher. The watchers serve as async guardians of the test flow, making sure the odd dialog window doesn't completely frustrate your tests. Having a tool that automatically watches your back when you're focused on the functional flow of your tests is awesome. 100% pure awesomesauce. Since the API documentation on watchers is scant and the UI Testing tutorial on the Android dev guide doesn't cover their use in depth, I figured I should add a post here that goes over a simple scenario demonstrating how to use this fundamentally important UI automation tool.

In my example code below, I'm using uiautomator to launch the API Demo app (meaning run this against an Emulator built in API level 17 - I used the Galaxy Nexus image included in the latest ADT and platform tools). The te…

Jenkins + Devices + AndroidJUnitRunner

New Android build system and test runner, same goose chase using undocumented features and hacks

As I've posted before, I am a big fan of Jenkins. It is extremely flexible, open source, and supported by a staggering array of plugins actively developed by engineers running over 100,000 instances of the server worldwide. With it's distributed node model, you can even build your own device cloud for hosting enterprise-scale automation, economizing hardware investments by sharing resources across multiple projects as well as speeding up automation by parallelizing test runs. I had been using a Jenkins-based system in the past to support instrumentation automation with Robotium quite happily. For the last couple years however, my work hasn't required that as much and I've found myself doing a lot more manual testing and using UiAutomator which didn't require a tight integration between the product codebase and the test code. As a result, I've been slow to adopt the…

Run-As Like the Wind: Getting private app data off non-rooted devices using adb run-as and a debuggable app

"You're some kind of big, fat, smart-bug aren't you?"
~Johnny Rico, Starship Troopers (1997) One of the most important things about writing bugs is making them descriptive but concise. Screenshots, video, debug logging, and hardware snapshots are all fairly standard or available to Android testers these days and can save you enormously on text when communicating what constitutes the bug. Sometimes though, the app gets into a weird state due to some transient data issue where you may not own the API or the complexity with forcing the app data into a certain state is prohibitively high. In those cases it is very handy to directly inspect the data the app has in its own directories.

Getting at this data is trivial on emulators and rooted devices but due to file system permissions, this data is otherwise completely private to the app itself. If you're like me, you tend to test using devices rather than emulators and you probably prefer not to root your devices since t…