Tag: cucumber

Contributing to Open Source – Getting Started

A sign in LegoLand saying "Adventure Land"

Recently, I started to contribute more to open source projects I use and like. When I started to contribute at all, I did so, with the most obvious thing for me: Bug reports. 🙂 This can be particularly useful, since the bug reports that I write for my projects, stay behind closed doors. – Like it or not, companies producing commercial software very often don’t fancy public bug reports. That’s a long story for another time…

Here are some tips:

  • Start small. Probably smaller than that.
    You can start on documentation. ➙ It can be unclear or contain typos.
    My smallest contribution so far, was fixing a typo in the Cucumber documentation.
    The steps to deliver it were large, compared to the fix itself:
    • Fork a repository
    • Create a branch on the fork
    • Fix the typo
    • Create a pull request
    • Get it accepted
      This seems like a lot, just to fix a typo.
      However, I think the learning experience was worth it.
  • Learn about the two most used models used in open source projects, the “Fork and Pull Model” and the “Shared Repository Model”.
    Both are explained in the GitHub docs about “collaborative development models“.
  • Check out Marko Denic on Twitter. He wrote “Make Your First Open Source Contribution” which explains to to get started on GitHub.
    You can contribute to his site https://tech-blogs.dev by adding you own blog, if you have one. The project is hosted on GitHub.
  • Start in a friendly & helpful community
    I find this really important: When you have a safe place to ask all the question you may have, and can expect a friendly answer, that will help actually asking those questions. It certainly helped me, asking about which development model is used, how big or small a pull request should be, etc.
  • Create your own project and make it open source
    Again: You can start really small. My smallest GitHub repository is probably https://github.com/s2k/seasidetestings-iterm-colours. It’s just only file that can be used as a colour preset in iTerm2 and some documentation how to load the file in iTerm.
  • If you find that something can be improved or isn’t working as expected, provide a bug report. These are important contributions to any software project: If the people writing the software don’t know that something’s wrong with it, they can’t fix it.
    For example I wrote https://github.com/fakefs/fakefs/issues/224, when I noticed that this Rubygem didn’t worked the way I expected.
    Since I knew how to write the RSpec to check this, I did – and it probably helped to get it fixed.

If you’re looking for much more information about how contributing to open source this book may be for you: “Forge Your Future with Open Source” by VM (Vicky) Brasseur.

The Computer Is Always Right

The other day I had trouble getting a Cucumber scenario to work. Here’s what happened, partly to have a post I can come back to, when (!) my future self runs into a similar problem.

I am using Cucumber and a very stripped down version of the project looked like this:

% tree
.
└── features
    ├── example.feature
    ├── step_definitions
    │   └── step_def.rb
    └── support
        └── env.rb

All code is entirely contrived and tailored to demonstrate the issue I bumped into. The example file features/example.feature first:

Feature: What is happening?

Scenario Outline: Matching step definitions -- or not

Given a step that mentions '<a_string>'
And another step that uses <a_number> like this
Then all is good

Examples:
|  a_string |  a_number |
|      word |        42 |
| two words | 3.1415927 |

There are very plain step definitions too (in file step_definitions/step_def.rb):

Given("a step that mentions {string}") do |string|
  pending # Write code here …
end

Given(/another step that uses ((\d+)|(\d+\.\d+)) like this/) do |number|
  pending # Write code here …
end

Then("all is good") do
  pending # Write code here …
end

Running this gives the expected result: Cucumber kindly informs that there are pending steps:

% bundle exec cucumber
Feature: What is happening?

  Scenario Outline: Matching step definitions -- or not # features/example.feature:3
    Given a step that mentions '<a_string>'             # features/example.feature:5
    And another step that uses <a_number> like this     # features/example.feature:6
    Then all is good                                    # features/example.feature:7

    Examples:
      | a_string   | a_number  |
      | word       | 42        |
      | two words  | 3.1415927 |

2 scenarios (2 pending)
6 steps (4 skipped, 2 pending)
0m0.012s

However at some point, I started getting another result (comments added by cucumber removed):

% bundle exec cucumber
Feature: What is happening?

  Scenario Outline: Matching step definitions -- or not
    Given a step that mentions '<a_string>'
    And another step that uses <a_number> like this
    Then all is good

    Examples:
      | a_string   | a_number  |
      | word       | 42        |
      | two words  | 3.1415927 |

2 scenarios (2 undefined)
6 steps (4 skipped, 2 undefined)
0m0.009s

You can implement step definitions for undefined steps with these snippets:

Given("a step that mentions {string}") do |string|
  pending # Write code here that turns the phrase above into concrete actions
end

Wait. What?!? I stared at the existing step definition for a while, comparing it with the one printed in the message above:

Given("a step that mentions {string}") do |string|
  pending # Write code here …
end

Confusion and disbelief kicked in.

One of the principles I use is this:

The computer is always right.

— Not sure where I picked this up. If you know the (or a) source, tell me please.

This is true even if the behaviour is wrong. In this case: If cucumber cannot find a step definition … it CANNOT find a step definition. But why would that be? Why did it happen in this particular case?

I even called in colleagues (remotely) and we stared at the code collectively. Still nothing.

Luckily a trace of a previous successful run was still available in the console output. So I copy-and-pasted the scenarios, and compared them piece by piece in a Pry session:

% pry
[1] pry(main)> works = File.read 'features/works.feature'
=> "Feature: …"
[2] pry(main)> broken = File.read 'features/broken.feature'
=> "Feature: …"
[3] pry(main)> works == broken
=> false

So there is in fact a difference. But what? Where?
Using the same pry session we found out:

[4] pry(main)> works.each_codepoint.zip(broken.each_codepoint).select{|el| el[0] != el[1] }
=> [
    [0] [
        [0] 32,
        [1] 160
    ]
]

What this does: The codepoints of both strings are combined in pairs, and then the pairs that are different are selected. Here’s what the Ruby Documentation says about each_codepoint:

Passes the Integer ordinal of each character in str, also known as a codepoint when applied to Unicode strings to the given block. For encodings other than UTF-8/UTF-16(BE|LE)/UTF-32(BE|LE), values are directly derived from the binary representation of each character. If no block is given, an enumerator is returned instead.

“hello\u0639”.each_codepoint {|c| print c, ‘ ‘ }

produces:

104 101 108 108 111 1593

https://ruby-doc.org/core-2.7.1/String.html#method-i-each_codepoint

The small piece that changed the behaviour was a space, just not a ‘normal’ space, but a NO-BREAK SPACE (see, for example, https://en.wikipedia.org/wiki/Non-breaking_space for more about this topic).

Lessons I learned (again):

  1. Some problems are hard to see, and in this case it was even invisible.
  2. The message was correct: The step was not defined, it only looked (to the human eye) as if it was.
  3. Using <spacebar> gives a different result than <option>-<sapacebar>.

Navigation

%d bloggers like this: