About README.md on GitHub

I ran across this wonderful post by Richard Kim, largely about doing a better job on GitHub README.md files.

Changed my life. I’m doing major reworks.

Advertisements

Comes Now, DebugHelper

I have no idea how many times I’ve typed code like this (to do printf-style debugging):


my_hash.each_pair do |key, value|
  p [key, value]
end

I’ve finally wised up, and built a helper method to support this:

DebugHelper.printf(data)

The method allows any data, and specifically supports Hash-like and Array-like structures.

It also allows an optional name (defaults to data class-name) and message.

Typical outputs:


Hash (size=3)
  a => 0
  b => 1
  c => 2

Array (size=3)
  0: a
  1: b
  2: c

And here’s my helper class:

# Class to help in 'printf' debugging.
class DebugHelper
  def self.printf(data, name = data.class.to_s, description = '')
    size = data.respond_to?(:size) ? data.size : 1
    puts format('%s (size=%d) %s', name, size, description)
    case
      when data.respond_to?(:each_pair)
        # Hash-like.
        data.each_pair do |k, v|
          puts format('  %s => %s', k, v)
        end
      when data.respond_to?(:each_with_index)
        # Array-like or Set-like.
        data.each_with_index do |v, i|
          puts format('  %6d: %s', i, v)
        end
      else
        puts format('  %s', data.inspect)
    end
    nil
  end
end

Test Automation Sherpa

“Test Automation Professional / Zealot” is the title I have in my resume.

But I’m not a tester, and still less a “quality” engineer.

Then what am I?

To borrow from mountain-climbing vocabulary, I’m not a climber, I’m a sherpa — one who does much of the heavy lifting, creates base camps, and keeps the climbers progressing happily and safely.

In that spirit, I aim to make it possible for others to do automated testing easily and reliably.

For a year now I’ve worked on my GitHub project, RubyTest, which embodies what I’ve learned in long years of building test automation.

It includes:

  • Core (application-independent) support:
    • Base classes
    • Helper classes
    • Logging
    • Reporting
  • Unit testing for the core (of course!).
  • Example domain-specific code:
    • Page objects, for web UI testing.
    • Endpoint objects, for TEST API testing.
    • Data objects, for both types of testing.
  • Example domain-specific tests.

Most recently, I’ve been building a Tester Tour of part of the project — the part that demonstrates testing for a REST API and a web UI. (The demo test targets are GitHub’s own REST API and web UI.)

TOURISTS WELCOME

You can see the tour here.

Any feedback appreciated, either as Issues on GH, comments here, or private email.

Hash (In)equality

In my automated testing, I often want to test a pair of hashes for equality. If the pair is equal, well enough.

But if they’re not equal, the simple way to record that is to log the failure, along with the two hashes. If the hashes are very small: I can visually compare them to determine the differences.

But for larger hashes, I can’t easily determine the differences visually. That’s where my class HashHelpler comes in.

It has method HashHelper.compare(expected, actual) that accepts the expected and actual hashes, and returns a hash having four keys and their corresponding values:

  • :ok: value is a hash containing the key/value pairs that are in both expected and actual.
  • :missing: value is a hash containing the key/value pairs that are in expected, but not in actual.
  • :unexpected: value is a hash containing the key/value pairs that are in actual, but not in expected.
  • :changed: value is a hash detailing the keys that are in both expected and actual, but whose values differ.

So: in my method Log#verdict_assert_equal?, a failed hash comparison gets and logs the detailed differences, making it easy to see what’s what.

Links:

Method Name As Documentation

Hey, Ruby coders!

Do you recognize this idiom?

Object.const_get(class_name)

Or this one?

Object.const_get(class_name).new(*args)

When I wanted to do these two things in my RubyTest project, I had to Google to find out how.

Now if I put this code into my project, will I recognize these idioms later on? Next month? Next year?

I could add comments to explain, but a comment can get stale (not keep up with code changes), or get separated from its code, or even get deleted.

You can help your downstream code enhancer/maintainer by pushing an unusual idiom into a well-named method.

(Hint: If you’re not sure who is the downstream enhancer/maintainer, it’s you!)

Thus, I created this:

class ObjectHelper

  def self.get_class_for_class_name(class_name)
    Object::const_get(class_name)
  end

  def self.instantiate_class_for_class_name(class_name, *args)
    self.get_class_for_class_name(class_name).new(*args)
  end

end

PS: My GitHub project is about test automation in Ruby. It has a Tester Tour of the demo testing for a web UI and for a REST API.

Reviewers Needed

I am pretty well finished with the Tester Tour of the example testing for the GitHub API.  It shows, from the tester’s point of view, how the REST API testing framework works.  This is part of my own GitHub project, RubyTest.

I’ll be grateful for any reviewers’ comments, which can be created as Issues on the RubyTest project itself (best, b/c records!), or can be emailed directly to me at burdettelamar@yaho.com.

Links:

 

Keeping the Documentation Green

Everyone loves a good example in the documentation.  Often, in the software world, the example is code.

Fair enough.

But does that example code actually work?  If it did work at some point in the past, does it still work?

The only way to know for sure is to run it!

Over at my GitHub project, RubyTest, I’m building a tester ‘tour’ of part of the project.  Each ‘stop’ in the tour consists of a small test (code) and its output (a log).

Each time I do a build of the tour, the build procedure executes each test and captures its refreshed log.  These are both plugged into a text file that becomes the markdown page for a tour stop.

So I always know that the test code still works!

Check it out:

Behold, the Endpoint Object

In testing web applications, the page object design pattern has become justifiably famous. Its job is classic data hiding: each such object encapsulates an HTML page.

When I began working on my first framework for testing a REST API, I asked myself how, if at all, this encapsulation principle applies there. And the immediately obvious answer is: the endpoint object.

Just as the page object encapsulates an HTML page, so does the endpoint object encapsulate a REST API endpoint. Each endpoint has its own encapsulating class.

Now an endpoint does not have a name, exactly, but it does have an HTTP method and a URL. I’ve used those to construct the endpoint class name.

Examples (from my framework for testing for GitHub’s own REST API):

Method and URL Endpoint Class Name Effect
GET /labels GetLabels Get all labels.
POST /labels PostLabels Create a label.
GET /labels/:name GetLabelsName Get the named label.
PATCH /labels/:name PatchLabelsName Update the named label.
DELETE /labels/:name DeleteLabelsName Delete the named label.

A test framework should make things simple for the tester, right? To that end, the methods in these objects accept and return actual Ruby Label objects, not raw JSON. The methods transparently handle the transforms between JSON and those objects.

Each endpoint has four such methods. Using PatchLabels as an example:

  • PatchLabels.call(client, label) creates a label and returns the created label as a Label object.
  • PatchLabels.call_and_return_payload(client, label) does the same, but returns both the Label object and the raw JSON payload (in case the caller want to examine it).
  • PatchLabels.verdict_call_and_verify_success(client, log, label) creates a label and returns the created label as a Label object, also logging relevant verdicts.
  • PatchLabels.verdict_aberrant(client, log) accesses the endpoint with various error-causing aberrations, logging relevant verdicts, and returning nothing.

Voilà, the endpoint object!