Using Ruby Metaprogramming to Output Timestamped Kibana Logs in Your RSpec Tests
At ITV, we verify the behaviour of our Scala micro-service API using core Ruby and RSpec. In order to Continuously Deliver changes to each micro-service, we test individual commits (forming a “candidate service”) in isolation against its proven stable counterparts (from prior runs of this very process).
Consequently, test failures can usually be narrowed down to a failure in an individual micro-service or in the contract between two or more micro-services.
As we use Logstash/Kibana across our environments, we can supplement test failures with a holistic view of the system when those failures occurred.
Of course we only want to see the Kibana logs that encompasses the failed test (and all relevant hooks), otherwise we’ll just be dealing with noise.
RSpec Hooks
In order to solve our problem, we need to do the following:
- Initialise a variable with the time before the entire test suite begins.
- Update the variable after each individual test (so that all
before
test hooks are taken into account). - Use the variable defined in (1) if it’s the very first test that fails or (2) if it’s for any test afterwards.
Unfortunately, the before(:suite)
hook - which, as the name implies, runs once before your entire suite of tests -
does not allow instance variables defined in that scope to be used in your tests.
Results in:
How about a class variable instead?
This now works but will result in Ruby runtime warnings every time the class variable is accessed:
So not the ideal solution.
Enter class_variable_set
Instead, using Ruby metaprogramming and class_variable_set we can modify a variable that holds a timestamp in a class that hasn’t been created yet.
When we do need to create an instance of this class (i.e. when we get a test failure), it will be initialised with the
timestamp from the last after
hook (or from the intial before(:suite)
hook if the very first test fails):
Alternatively, you can change start_time
to be a class method instead:
Which means that you can use start_time
directly on the GlobalDateTime
class, rather than creating a new instance
first:
Comments
comments powered by Disqus