Author: Stephan

About Stephan

I'm a self-employed software tester. I test and write software, listen to music and go hiking — sometimes simultaneously. Prior to this was a physicist and oceanographer, among other things.

Tips for Conference Proposals & Sessions

Disclaimer: This year I’m one of the reviewers for the Agile Testing Days.

It’s proposal season again, at least for one of my favourite conferences, the Agile Testing Days; the call for papers is open until March 28th (for 2021).

There is a good number of articles and blogs available on the topic of Agile Testing Days proposals alone: I suggest to follow the advice Uwe gives with his blog post “Call for Papers Submission Pitfalls & How to Do it Better! Tales from a Conference Organizer“. There’s more info to find from that same conference over on YouTube: https://youtu.be/QxC-Ee51xmM

Mind The Context

Another important tip (especially for those thinking about a keynote) comes from Liz Keogh:

If it’s an opening keynote, I try to open people’s minds to learn and question. If it’s a closing keynote, I try to help them reflect on what they learned. Keynotes are there to frame the rest of the conference.

Liz Keogh on Twitter

I believe similar thinking applies to other sessions too: Make your session fit into the frame of the conference and what you know about the attendees expectations.

Tell a Story

I personally try to at least come up with a story, either one I made up or a personal one. Both worked well for me:

For a ½-day tutorial about testing and the Internet of Things (IoT), I set up the story of Goblin King Jareth the 42nd, who wanted to control his kingdom with a set of IoT devices and tasked the participants with testings these devices. The framing story helped to provide some reason to actually participate in the exercises.

For a (read: ‘the’ 🙂) keynote I gave at Agile Testing Days 2019, I chose the most personal story I possibly could: My diagnosis of and treatment for cancer – and why I still consider myself lucky. Read about it in “Being Lucky — A Keynote at the Agile Testing Days 2019“.

If you like to know more about telling a good story, be sure to read Huib Schoots’ blog post “Storytelling“! You’ll find — no surprise — a good story (and many links to more information, too).

Be Prepared

Things can go wrong: The notebook you planned to give the presentation with may crash. The projector may break or the sound system my fail. You may forget what you wanted to say. These things all happened to someone somewhere.

It’s better (and impressive!) to be prepared. For my keynote, I prepared index cards with notes of what I wanted to say when, when to make extra long breaks and other instructions, such as when to proceed to the next slide. At first I numbered them, so I could sort them, if I dropped them on the floor.

Numbered index cards

Later, I also put them on a thread: Now, even when I would drop them, they would still be sorted! This would have saved the talk, had I dropped those cards.

Index cards on a thread, to prevent shuffling

Some of the directions didn’t work out in the moment the presentation was live: The introduction was totally different from what I expected (and significantly louder!).

Mind the Last Possible Moment

One important, even obvious, aspect: Don’t miss the dead line. I did once and it’s annoying. Very annoying. — Most of the work was done for nothing, because I forgot to check the calendar. I learned it the hard way: If I’m too late to submit, it doesn’t matter how good the proposal was. Only if submitted within time, a paper has a chance to be selected.

I hope to see many great and inspiring proposals for the Agile Testing Days 2021!

Collecting Lists In a Ruby Hash and the ‘<<=' operator

The other day, I needed to quickly analyse a data set that came in form of a large CSV file. I wanted to collect a particular column of that table and collect all entries categorised by a key in another column.

A simplified version of the table could look like this:

Key Value1 Interesting_Value Other_Value
foo1723.5X
bar211.75Q
foo4212.6B
baz2717.8F
bar4947.2K

I strived for something like this:

result = { 
  foo: [23.5, 12.6],
  bar: [1.75, 47.2],
  baz: [17.8],
 }

Iterating over the rows is easy, and getting to the columns is no problem either: The CSV gem is well documented and supports this easily.

A nice way to accumulate data is Enumerable#each_with_object. Since I wanted the result to be grouped by a key value, I’d pass a Hash as the initial argument.

Step 1: each_with_object({})

However, since I’ve planned to append values for changing keys, the default value needed to be an Array, not the default of nil.

Step 2: each_with_object(Hash.new([])

This, however, returns the same empty Array, when a key isn’t found, but I wanted a new empty Array:

Step 3: each_with_object(Hash.new { [] })

This executs the block every time a default values is needed (i.e. the given key isn’t yet in the Hash).

The next step is to append the value found in a row to the (potentially new and empty) Array for the given key.

I thought it would work this way:

data_table.each_with_object( Hash.new { [] }) do |row, acc|
  acc[row['Key']] << row['Interesting_Value'] 
end

But, no, the result of this code is an empty Hash! It needs to be the <<= operator to work, as shown in the snippet of a pry session:

[2] pry(main)> data_table = CSV.read 'table.csv', headers: true
=> #<CSV::Table mode:col_or_row row_count:6>
[3] pry(main)> data_table.each_with_object( Hash.new { [] }) do |row, acc|
[3] pry(main)*   acc[row['Key']] <<= row['Interesting_Value']
[3] pry(main)* end
=> {"foo"=>["23.5", "12.6"], "bar"=>["1.75", "47.2"], "baz"=>["17.8"]}

It seems to me, that the Hash lookup with the given default value [] returns an Array, and the append operator << does in fact append the passed object to that Array, but then the result of that does not end up as a (new) value fo the given Hash key. In contrast, the <<= operator does assign the result of the append operation.

Seeing And Saying On The Command Line

Occasionally I like command line tools to also say the output, in addition to ‘printing’ it (to STDOUT).

A current context is starting a server locally with updated information. The start up takes time, so I tend to not wait at the command line for it to fully boot.

I wanted a noticeable signal when the thing is up – and also a confirmation that the version of the information source *is* actually updated. The commands tee and (the macOS) say do this for me:

tee >(say&)

tee copies the output of its input to STDOUT (usually the terminal) and the given file. The construction >(other command&) causes tee to send the input not to a file but to othercommand and the & causes that command to return immediately (instead of returning only after the command to finish).

Overall, I do something like this. The system returns a lengthy JSON string, so I use jq to get to the version information I’m looking for:

curl -s -u <username:thepassword> “<url_to_locally_running_service>” | jq “<path_to_version_info_in_json>” | tee >(say&)

The GitHub page explains that

jq is a lightweight and flexible command-line JSON processor.

https://stedolan.github.io/jq/

It’s well worth trying, if you’re dealing with JSON input on the command line.

Passing Multiple Arguments To One Rake Task

Occasionally, I want Rake tasks to take multiple arguments. If the number of these arguments are know beforehand, Rake provides a nice way to express that in the Rakefile (see the Rake documentation for more details).

However, sometimes I like to pass a varying number of arguments the a task, which shall all be processed in the same way. Maybe its user IDs that need to be removed from a database, or a given list of piles to be processed.

Today I found a way to do this, by using Rake::TaskArguments#extras . The method extras returns a list of all the extra arguments passed to a Rake task, that are not named in the task definition. If no argument name(s) are declared in the task definition, then all arguments will be ‘extra arguments’.

An example Rakefile is this:

task :list do |t, args|
  puts "In task '#{t.name}"
  puts 'Passed arguments:'
  puts args.extras.map { |extra| "  * #{extra}" }
end

Calling this task from the command line and its output (in zsh) looks like this:

$ rake list\[one,2,3.141]
In task 'list
Passed arguments:
  * one
  * 2
  * 3.141

Note, the \ before the opening bracket ‘[‘. In zsh, this is needed to escape the ‘[‘ (pairs of ‘[]’ have a special meaning in zsh). Also note, that there are no spaces between the arguments, just the comma.

If you prefer to add a space after the comma, there’s a way to achieve this (again in zsh): Drop the ‘\’ and instead put the task name and the arguments in quotes:

$ rake "list[one, 2, 3.141]"
In task 'list
Passed arguments:
  * one
  * 2
  * 3.141

$ rake 'list[one, 2, 42]'
In task 'list
Passed arguments:
  * one
  * 2
  * 42

In this example it doesn’t affect the output whether single or double quotes are used. Quoting in shells is … complicated, see for example: http://zsh.sourceforge.net/Guide/zshguide05.html#l112.

Did you find this helpful? Or do you know a better way to pass multiple arguments to one Rake task? Please let me know.


Generating a Preview on LeanPub Using Rake

While working on the e-books I published on LeanPub, I have developed a number of useful approaches to get fast(er) feedback on how the book looks. Two earlier blog posts describe some of this:

While this provides a nice feedback cycle, sometimes I like to generate a new preview without pushing to the GitHub repository I am using to share the book content with LeanPub. This happens, when I change settings on the Leanpub site that affect the generation of the book (the title image, font faces and sizes are set via the book’s pages on LeanPub, not a configuration file in the repository).

While I could click thought the UI and navigate to the page where I can generate a new preview, I prefer using a command line tool from my local machine: Rake

The Setup

A warning: The LeanPub API documentation says: “Using the Leanpub API requires a Pro plan.

To use the LeanPub API, an API key is needed. The link to the API documentation above has information where to get that key. To easily use this API key, I store it in an environment variable LEANPUB_API_KEY.

The Rake Task Definition

In the repository of my book I have a Rakefile containing the task definitions. Here’s the one to trigger the generation of a preview on Leanpub:

require 'rest-client'

namespace :leanpub do
  LEANPUB_BASE_URL = "https://leanpub.com/<book_id_on_leanpub>"
  namespace :preview do
    desc "Generate new Preview on LeanPub"
    task :generate do |t|
      what_to_generate = t.name.split(':')[1]
      url = "#{LEANPUB_BASE_URL}/#{what_to_generate}.json"
      begin
        RestClient.post url, api_key: ENV['LEANPUB_API_KEY']
      rescue RestClient::Exception => e
        puts "Got error #{e.message} in", caller.first
        exit 1
      end
      puts "Generation of preview was triggered"
    end
  end
end

I can now easily generate a preview, without having to leave the IDE I’m using to write the book (or the command line) using this:

$ rake leanpub:preview:generate
Generation of preview was triggered

The Rakefile will likely change, for example to also support publishing a new version of the current ebook, or to make it more flexible in order to handle different ebooks.

Update (8. Jan 2021): It turns out that this is really useful: LeanPub provides a web hook to generate a _sub set_ of the book that also generates the PDF (but not the ebook and mobi file). This saves some time from pushing to GitHub to being able to review the generated file. I now use this web hook most of the time.

I now only generate for full book in all formats when I want to check that the book looks good enough to be published. Since this happens less regularly than pushing to the repository, I use the Rake task.

Navigation

%d bloggers like this: