After reading the title Embedding a Test Suite in a Single-file Ruby App (Part 1) I thought: aha! finally somebody has done it and used ruby data block to store tests. However I was disappointed as there was another approach, which I think is less elegant.
Below I show you a proof of concept of embedding tests in ruby code.
Because tests and code are in the same file:
- easier to refactor
- easier to navigate
- documentation from tests near the code
- does not affect code performance
- reminds you to write tests
Ruby has special feature which I call data blocks. In short you can embed any data after your ruby code in the same file and when you run that file that data will be available as
DATA constant. All you need is to separate it with
$ cat t.rb puts DATA.gets __END__ hello world! $ ruby t.rb hello world!
Putting all together
Below is my proof of concept to run rspec tests from the same file.
# drink.rb class Drink attr_reader :type def initialize @type = "water" end end __END__ require 'spec_helper' RSpec.describe Drink do it 'is has default type water' do expect(Drink.new.type).to eq('water') end end
In order for that to work we need to put small patch to rspec so it reads the file correctly.
# spec_helper.rb class DataBlockLoader module RspecConfig def load_spec_files super files_to_run.uniq.each do |f| file = File.expand_path(f) DataBlockLoader.load file end end end END_TOKEN = /^__END__\r?\n/ def self.load file_path io = File.open(file_path, 'r' ) loop do line = io.gets break if line.nil? break if line.match(END_TOKEN) end line_no = io.lineno eval io.read, nil, file_path, line_no + 1 end end RSpec::Core::Configuration.prepend DataBlockLoader::RspecConfig
Now we can just run that file with rspec:
$ rspec drink.rb 1/1 |========================= 100 ==========================>| Time: 00:00:01 Finished in 0.86 seconds (files took 0.70232 seconds to load) 1 example, 0 failures
This is just a small proof of concept which requires more work on adopting rspec internals to work with data blocks. Let's me know if you'll take a challenge on that.