The project is in a healthy, maintained state
Parameterized testing utility
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

ruby_parameterized_testing

Parameterized testing utility for Ruby.

Installation

Add test dependency in your Gemfile.

group :test do
  # For RSpec
  gem "parameterized_testing-rspec", "~> 0.2"

  # For minitest/spec
  gem "parameterized_testing-minitest-spec", "~> 0.2"
end

Usage (with RSpec)

With this gem, parameterized tests can be written as follows:

# parameterized_testing-rspec example
RSpec.describe "something" do
  # #parameterized is available in the context of RSpec ExampleGroup.
  parameterized(:a, :b, :result) do
    # Declare the concrete input corresponding to the parameters with #input.
    input { [1, 2, 3] }                  # shorthand syntax
    input { { a: 4, b: 5, result: 9 } }  # verbose syntax
    input { ["foo", "bar", "foobar"] }
    ...

    # Other than that, we can write tests in the same way as a conventional RSpec ExampleGroup.
    subject { a + b }

    it { is_expected.to eq result }
  end
end

How #parameterized works

It is important to notice that #parameterized collects inputs by actually instance_executing the block in a dedicated context in order to embed the line number of input to #context. In this context, all other method calls are ignored.

If you want to perform some extra procedure in the context of RSpec ExampleGroup, place them outside of #parameterized.

Differences with rspec-parameterized

There is already a gem called rspec-parameterized that supports parameterized testing.

parameterized_testing does not depend on parser, proc_to_ast, or unparser. Code like the example above is conceptually1 expanded as follows:

RSpec.describe "something" do
  sig_a_b_result = ::ParameterizedTesting::Signature.new(:a, :b, :result)

  context 'input[0] at line 6' do
    let(:_input_a_b_result) { sig_a_b_result.map([1, 2, 3]) }
    let(:a) { __send__(:_input_a_b_result).fetch(:a) }
    let(:b) { __send__(:_input_a_b_result).fetch(:b) }
    let(:result) { __send__(:_input_a_b_result).fetch(:result) }

    subject { a + b }

    it { is_expected.to eq result }
  end

  context 'input[1] at line 7' do
    let(:_input_a_b_result) { sig_a_b_result.map({ a: 4, b: 5, result: 9 }) }
    ...
  end

  context 'input[2] at line 8' do
    let(:_input_a_b_result) { sig_a_b_result.map(["foo", "bar", "foobar"]) }
    ...
  end

  ...
end

Since each input is declared as a block, there are no helpers such as ref or lazy, which were in rspec-parameterized.

Usage (with minitest/spec)

You can use the parameterization block in the same way as in the RSpec example.

describe "something" do
  parameterized(:op, :result) do
    input { [->(x, y) { x + y }, a + b] }
    input { [->(x, y) { x * y }, a * b] }

    subject { op.call(a, b) }

    parameterized(:a, :b) do
      input { [2, 3] }
      input { [5, 7] }

      it "tests something" do
        _(subject).must_equal result
      end
    end
  end
end

Development

$ git clone https://github.com/yubrot/ruby_parameterized_testing
$ cd gems/parameterized_testing
$ bin/setup
$ bundle exec rake --tasks

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/yubrot/ruby_parameterized_testing. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Footnotes

  1. The real implementation can be seen in driver.rb, for example. ↩