On this page:

Cucumber, Capybara, and the joys of integration testing in Rails

Development is so much more fun with test cases. They give you a big target to aim for, and it feels fantastic when you write the code to make them pass. Tests also avoid or shorten those late-night “oh no! I broke something!” sessions, because you can backtrack to versions that pass the tests. (You are using version control, right?)

So naturally, as I worked on my first IBM project using Ruby on Rails, I wanted to know about how to perform automated testing – not just at the unit level, but at the web/integration level.

I like using Simpletest in Drupal. I love the testing frameworks available in Rails.

You see, Cucumber for Rails allows you to write your tests in English (or something reasonably close to it). For example:

Feature: Contributor
  In order to maintain security
  As a contributor
  I want to be able to edit existing submissions
  Scenario: Contributor should not be able to create or delete submissions
    Given I am a company contributor
    And there is a 2010 survey for "Company X"
    When I view the dashboard
    Then I should not be able to delete a submission
    And I should not be able to create a submission

Putting that in my features/contributor.feature" file and executing that with =bundle execute cucumber features/contributor.feature gets me a lovely test with green signs all around.

You’re thinking: Rails is awesome, but it’s not that awesome, is it? How can it know about the specifics of the application?

Rails knows because I’ve written my own step definitions for Cucumber. Step definitions are simple. You can define them with a regular expression like this:

When /^I view the dashboard/ do
  visit root_path
end

Then /^I should not be able to create a submission/ do
  page.should_not have_button("Create submission")
end

You can also define steps that parse arguments from the string or call other steps:

Given /^there is a ([^ ]+) survey for \"([^\"]+)\"$/ do |year,name|
  @company = Company.find_by_name(name)
  assert !@company.nil?
  Given "there is a #{year} survey"
end

You can even take multi-line input, such as tables.

Automated testing is so awesome!

Helping kids learn about automation

J- shuffled in and out of the living room, listless and bored. As part of a 9-week simulation of real life in school, she and her classmates had been assigned jobs. Her job was to be an accountant, and the tedium of checking dozens of pretend tax returns had long sunk in. W- had encouraged her to use a calculator, so at least she didn’t have to multiple all those figures by hand, but there were still so many numbers to verify.

My geek sense tingled, as it does whenever there’s an opportunity for a quick win through automation. I coaxed her back to her homework. “Come on, let’s set up a spreadsheet,” I said. “That way, you don’t have to redo each of the calculations or worry about getting things wrong.”

We brought up OpenOffice.org Calc. She was still lackluster, so I took the lead in creating the spreadsheet. I asked her which tax return we could use as a model, and she picked hers. We started filling in the formulas, checking her work along the way. (We found and fixed an error in her tax return, too!) Then we tested the spreadsheet on a few other tax returns she had manually done, and she used it to check the rest.

Result: Not only could she verify a correct tax return in less than a minute, but she perked up and started having fun with it. She made a pile of correct tax returns and a pile of incorrect ones, with sticky notes pointing out the deficiencies. She still doesn’t want to be an accountant again, but at least she knows that tedious tasks might be automated away.

The next time J- finds herself doing tedious calculations or verifications, I hope she thinks about how much faster, more reliable, and more enjoyable the spreadsheet was compared to calculating things step by step, and perhaps invest time into learning how to automate whatever she needs to do.

How do people learn how to automate? It’s such a time-saving skill, but it doesn’t seem all that common. Maybe people are intimidated by spreadsheets and programming languages, and that fear of losing more time keeps them from gradually building the knowledge they need to save lots of time. If we can show J- and other kids the benefits of automating, maybe that light at the end of the tunnel will encourage them to learn. If we expose them to the methods for automating tasks, such as putting calculations into a spreadsheet, creating keyboard macros, or writing short programs, maybe they’ll realize it’s not scary – and maybe they’ll start modifying or creating new tools.

In my experience, working with new automating frameworks is always slow and somewhat frustrating in the beginning. It helps that I don’t usually need or want to automate everything right away. I break things down into small things, small wins. I might start by figuring out the most time-consuming parts and automating that 10%, or automating the most common operations. As I become more familiar with the tools and the process, I automate a little bit more, and more, and more. Eventually I might even create a tool that other people can use, like the way my Community Toolkit for Lotus Connections is off and running.

The hardest thing, I guess, is knowing where to start. I run into that problem a lot, because I work with lots of different technologies and frameworks. It’s like looking for the end of a tangled piece of string. That can be hard to find in the confusion, but once you do, you can start unknotting the mess. I want J- to be able to think: ah, this has to do with calculations, maybe I can get a handle on it by using a spreadsheet, putting in manual steps if needed.

How do you use teachable moments to encourage people to automate?

2011-03-29 Tue 21:09

Saving team members from RSI

I watched Jen: Ctrl-C, Alt-Tab, click, click, Ctrl-v, click, click, click, click, click, Alt-Tab, Down, Ctrl-C… One by one, Jen copied the tasks from our Drupal planning spreadsheet to the Rational Team Concert task-tracking system.

I didn’t know if RTC had a batch import system we could use, but I’d rather do a macro using AutoHotkey instead of letting Jen copy the information one row at a time. (And with so many clicks, too!)

Fifteen minutes and some tweaking later, I have an AutoHotkey script that copies the information, creates the task, and moves to the next row. A few minutes, and I’ve copied all the rest of the tasks.

Less risk of repetitive strain injury for everyone, more interesting work, and the ability to easily handle future spreadsheets. Yay!

I show her the AutoHotkey script at work. “Coool,” she says.

Time to organize the tasks by story. Drag-and-drop to the rescue. Not easy with a mouse – Fitts’s law, small targets – but it’s easy enough with the tablet stylus. It feels natural.

Keep an eye out for the little things that you can fix with just the right tool. =)

AutoHotkey script:

F12::MakeRTCTask()
MakeRTCTask()
{
   SetTitleMatchMode,2
   CoordMode Mouse, Screen
   WinActivate, Planning
   WinWaitActive, Planning
   Send ^c
   Sleep 200
   WinActivate, IBM Rational Team Concert
   WinWaitActive, IBM Rational Team Concert
   Click 972, 346  ; add
   Sleep 500
   Click 927, 406  ; task
   Sleep 500
   Click 468, 154  ; summary text field
   Send ^v
   Send {TAB}{TAB}
   Sleep 100
   Send {DOWN}  ; filed against
   Send {TAB}{TAB}{TAB}{TAB}
   Sleep 100
   Send 1  ; priority
   Send 1
   Click 807, 125  ; save and close
   Sleep 500
   Send {PgUp}{PgUp}{PgUp}
   WinActivate, Planning
   WinWaitActive, Planning
   Send {ESC}{DOWN}
}

Old notes on staffing a virtual conference booth

It’s fantastic how a blog archive lets me pull up lessons learned from a virtual conference I helped at two years ago. Some of these tips from my internal blog post are platform-specific, but others might be useful.

Staffing the Social Networking booth at the Innovation in Action event. Here are quick tips:

  • Set up text shortcuts. You’ll need to type in a lot of text rapidly. The built-in Text Entries are not available when you’re sending an initial message or inviting someone to a chat, so type in some boilerplate text into Notepad and then copy and paste it. Messages you send from the booth will be marked as from your booth name, so include your name and e-mail address in your message. Advanced tip: use AutoHotkey to create a text macro. Install it from AutoHotkeyInstaller.exe, create a file like shortcuts.ahk (customize this of course), then double-click shortcuts.ahk to make it part of your system. Example shortcuts.ahk:
    ::!hello::Welcome to the IBM social networking booth. I’m Sacha Chua ([email removed]), a consultant who helps organizations figure out what Web 2.0 is, how it fits with their strategy, how to implement it, and how to make the most of it. Please feel free to ask me questions by sending a note or inviting me to chat. What can I help you with?
    ::!tapscott::Hello and welcome to the IBM social networking booth. I’m Sacha Chua ([email removed]), an IBM consultant who helps organizations figure out what Web 2.0 is, how it fits with their strategy, how to implement it, and how to make the most of it. What did you think of Don Tapscott’s keynote? Please feel free to start a chat if you want to talk about it or if you have any questions about social networking.

    After that, you’ll be able to type !hello into anywhere and have it expanded. To update, edit shortcuts.ahk and then double-click it again.

  • Check people’s visitor histories. The visitor history will tell you about any messages sent from or to this booth, if the visitor has been to this booth before, and so on. Great way to make sure you don’t send a message twice.
  • Send people messages and invite them to chat with you. You can initiate only one chat at a time, and you have to wait for the person to accept or reject the invitation before inviting another person. You can send as many messages as you want, though, and you can have as many open chats as you want.
  • Send yourself follow-up requests after conversations. Your goal in each conversation is to find out what people are interested and give yourself an excuse to follow up. After you get that, use the [i] button on the right (your chat partner’s profile) to display the profile, then use the Followup button to send yourself a copy of the person’s visitor history. WARNING: There’s some delay when selecting names from the list, so double-check that you’re sending the right person’s information.
  • Pull in experts. Need help answering a question? Tell the visitor you’re bringing someone in, then click on the expert’s profile, choose Invite to chat, and choose the chat session you want the expert to join.

Non-obvious things:

  • Your name will not be associated with any messages (from or to), so don’t count on being able to quickly see replies from people or find out what you sent someone.
  • The sorting buttons on the lists sort only the displayed entries, not all the entries. Entries will always be arranged chronologically, although in-page sorting may be different. Don’t count on being able to use this to see all the messages sent by visitors. Just leave it on Date.
  • If someone leaves your booth while you’re trying to check their visitor history, their info box disappears.
  • As people enter and leave the booth, odd things happen to the page. Be prepared to have to find people again.
  • Things get much quieter when people are listening to sessions. Eat or rest during those times.

Braindump: Automating repetitive tasks using AutoHotkey

Note for myself (because I’m going to need this again someday!), and for others who drop by:

I needed to copy information from 45 slides and put them into an Excel spreadsheet so that I could reorganize the content and put them into a wiki. Fortunately, the author of the Powerpoint deck used a fairly consistent slide format. I used AutoHotkey to copy most of the information over by simulating mouse clicks and button presses. I started with this macro, which copies the text, switches to my spreadsheet, moves a cell to the right, and pastes it:

F12::
Send, {CTRLDOWN}c{CTRLUP}
WinWait, Microsoft Excel - facilitation.xls, 
IfWinNotActive, Microsoft Excel - facilitation.xls, , WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{CTRLDOWN}v{CTRLUP}{ALTDOWN}{ALTUP}
WinActivate, Microsoft PowerPoint
return

 

I wanted to save even more keystrokes and mouseclicks, so I ended up automating the copying of each slide using the following script. It wasn’t perfect, but it saved me time and it was fun to make.

F11::
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left, 1037,  327, 1500, 327
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {CTRLDOWN}v{CTRLUP}{RIGHT}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left,  1037,  366, 1500, 366
Sleep, 100
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{CTRLDOWN}v{CTRLUP}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left,  457,  344, 1500, 1000
Sleep, 100
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{RIGHT}{CTRLDOWN}v{CTRLUP}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left,  454,  454, 1500, 1000
Sleep, 100
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{CTRLDOWN}v{CTRLUP}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left,  564,  535, 1500, 1000
Sleep, 100
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{CTRLDOWN}v{CTRLUP}{ALTDOWN}{ALTUP}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
MouseClickDrag, left,  490,  637, 1500, 1000
Sleep, 100
Send, {CTRLDOWN}c{CTRLUP}
WinActivate, Microsoft Excel - facilitation.xls, 
WinWaitActive, Microsoft Excel - facilitation.xls, 
Send, {RIGHT}{CTRLDOWN}v{CTRLUP}{LEFT}{LEFT}{LEFT}{LEFT}{LEFT}{LEFT}{DOWN}
WinActivate, Microsoft PowerPoint - [ID Methods.ppt], 
WinWaitActive, Microsoft PowerPoint - [ID Methods.ppt], 
Send, {PGDN}
return

Automation is worth the time investment. If you’re on Windows, check out AutoHotkey. =)

From delegation to automation

It had to happen. I’ve replaced my Timesvr assistants with a collection of Perl scripts. Delegation had been a good experiment, but I’d gotten frustrated by the number of duplicate calendar entries and the occasional library fine when people didn’t follow my instructions correctly, even with all the notes and clarifications I’d added. Also, my wake-up calls were no longer being done by happy, enthusiastic assistants, but by uncertain-sounding assistants who paused for approval all the time.

Being a resourceful programmer, I cancelled my monthly subscription and wrote code that did many of the routine tasks I’d asked them to do.

What worked well, and what can I improve?

  • Delegating tasks to more skilled professionals whom I’d picked myself actually worked quite well. I dropped several people who didn’t perform as well as I’d hoped, including an illustrator who submitted tracings of other people’s photos instead of drawing something original.
    It was good to think about which tasks I could delegate and what steps were involved.
  • Setting up one-time appointments worked well, but setting up recurring appointments was confusing because of Timesvr’s rotating pool of assistants. Next time, I’d probably go with having a single assistant if I want something like that.
  • Being able to call in with a request was a useful substitute for having a mobile data plan and looking things up myself, but a data plan is faster. ;)

Delegation was a good experiment, but automation is even more fun. I find myself thinking in Perl rather than Ruby because Perl’s archive of modules (CPAN) is much, much bigger than Ruby’s, so practically everything I want to do can take advantage of an existing library. =)