Formulaic: filling Rails forms with Capybara from a hash
Simplify form filling with Capybara
At a glance
- What is it?
- Formulaic is a Ruby gem that fills in a Capybara form from a hash of attributes instead of a sequence of procedural calls, and it finds the right fields by looking at the labels the way a person filling the form would. It depends on SimpleForm style translations, does not handle AJAX forms, and has not had a release since 2017.
- Who is it for?
- Formulaic fits a Rails team with SimpleForm and I18n translations already in place whose feature specs spend more lines clicking fields than asserting behaviour, and who want the specification to read like the data the form holds.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 100 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A hash of attributes instead of a sequence of clicks
The README states the problem in one line: remove the tedium of formulaic form filling with Capybara. Formulaic allows you to specify a hash of attributes to be input rather than procedurally calling Capybara's DSL methods.
The difference shows up in what a feature spec looks like. The README's example fills a registration form with one call:
feature 'New user registration' do
scenario 'successfull sign up' do
visit sign_in_path
fill_form(:user, { name: 'Caleb', email: '[email protected]', 'Terms of Service' => true })
click_on submit(:user)
expect(page).to have_content t('user.create.success')
end
endThe equivalent in raw Capybara would be a find for the name field, a fill_in, a find for the email field, another fill_in, and then a check on the terms of service, with each step depending on the markup of the form at that moment. The hash version says what the data is, and lets the gem work out where each piece goes.
That is the whole idea, and it is a narrow one. Formulaic is not a form builder and it does not change how your application renders forms. It is a test-side convenience layer that removes the mechanical part of a Capybara feature spec.
Installation is a single line in the test group of your Gemfile:
gem 'formulaic', group: :testThe primary language is Ruby, the licence is MIT, and the repository carries a gemspec, a Rakefile, a lib directory, a spec directory and a NEWS file. The default branch is main and the last push was on 2026-07-15.
The translation convention is the contract
The Assumptions section is where the design actually lives. Formulaic relies pretty heavily on the assumption that your application is using translations for SimpleForm and input helpers, using the `simple_form.labels.<model>.<attribute>` and `helpers.submit.<model>.<action>` conventions.
So the gem finds a field by looking up what that field's label is supposed to be, and then finding an input with that label. It is doing the same thing a person would do, which is why the README explains the limitation about duplicate labels later on.
That assumption is also the escape hatch. You can still use Formulaic by using strings as keys instead of symbols, which it knows to pass directly to `fill_in` rather than trying to find a translation. You'll need to find submit buttons yourself since `submit` is a thin wrapper around `I18n.t`.
The rule for keys is short. If an `attributes` key is a `String`, it will be used as the literal label. For `Symbol` the gem will attempt to translate, fall back to `human_attribute_name` if available, then call `to_s`.
The `human_attribute_name` fallback is the part that makes this work outside Rails' default labelling, because a model can override that method to produce whatever wording the interface uses. If your application has been localised carefully enough that the label on the screen differs from the model attribute name, that is the hook that reconciles the two.
Four methods, and one of them is a convenience
The public API is small and each method has one job.
`fill_form` provides an interface to completely filling out a form. The signature is:
fill_form(model_name, :new, attributes)Provide the `model_name` as a symbol and `attributes` as a hash of column name to database value, or label string to database value. The `:new` in that signature is the form variant, and the matching submit action is derived from it.
`input` gives an easy way to find the translated text of an input. It is primarily used internally to fill inputs, but is provided in the public API as it could be useful:
input(model_name, field)`submit` functions like `input`, but finds the translation for the submit button of the form, and `model_name` should be the same as what you provide to `fill_form`. Typically, the return value of `submit` will be passed directly to Capybara's `click_on` method. If you are submitting a form that is not for the `create` action, you may need to pass the action:
submit(:user, :update)The `model_name` and `action` should match up to the `helpers.submit.<model_name>.<action>` translations, so a form that submits an update needs both the right action and the right translation key.
The fourth method is the convenience one. `fill_form_and_submit` is effectively a `fill_form` followed by `click_on submit`, but smart enough to `fill_form` with `:new` and `submit` with `:create` and the edit/update cousin:
fill_form_and_submit(:user, :new, attributes)Nested forms are handled with an extra call rather than a special API. If you have nested forms, through `fields_for` or any variant, you are able to fill them with an extra call to `fill_form`:
fill_form(main_model_name, main_model_attributes)
fill_form(nested_model_name, nested_model_attributes)Including it in RSpec, Minitest and Capybara::TestHelper
Formulaic ships as a module you include, and the README shows the three setups.
For RSpec, in spec/spec_helper.rb:
# spec/spec_helper.rb
RSpec.configure do |config|
config.include Formulaic::Dsl, type: :feature
endThe `type: :feature` filter matters, because the module adds methods that would otherwise sit in every example group.
For Minitest or Test::Unit, in test/test_helper.rb:
# test/test_helper.rb
class ActionDispatch::IntegrationTest
include Capybara::DSL
include Formulaic::Dsl
endAnd for Capybara::TestHelper, either include the module in the base class:
class BaseTestHelper < Capybara::TestHelper
include Formulaic::Dsl
endor delegate the four methods explicitly, which is the narrower option:
class FormTestHelper < BaseTestHelper
delegate_to_test_context(:fill_form, :input, :submit, :fill_form_and_submit)
endThat last form is the one to consider if the helper class is shared with other tests that would not benefit from the extra methods.
Factory Bot gets a short mention because the combination is common. You can pass the factory's attributes straight in:
fill_form(:user, attributes_for(:user))And when the factory carries attributes that do not pertain to the form you are filling, the README shows slicing them:
fill_form(:user, attributes_for(:user).slice(*sign_up_attributes))
# ...
def sign_up_attributes
[:name, :email, :terms_of_service]
endThe slicing is worth noting, because without it the factory's extra attributes would be looked up as labels and would fail to find a field for them.
The value's class decides what happens to it
The Known Limitations section contains the most useful table in the README, because it documents the mapping from the `#class` of the attribute values to Capybara method calls, and it is honest that the table is not exhaustive.
A `String` maps to `fill_in`, `choose` or `select`, which means a string value can end up in a text field, a radio button or a select box depending on what the form contains. A `Date` or an `ActiveSupport::TimeWithZone` maps to `select` for year, month and day, which is the sensible approach for a date picker made of three selects.
A `TrueClass` maps to `check` and a `FalseClass` maps to `uncheck`, which is why the example form can pass `'Terms of Service' => true` and have the checkbox ticked without any special handling. An `Array` maps to `check` or `select` each array member, which should all be strings, and if not all items can be selected or checked, an error will be thrown. A `File` maps to `attach_file` with `File#path`.
The array behaviour is the one to be careful with. It is a multi-select helper, not a general collection filler, and the README is explicit that the members should all be strings and that a partial match is an error rather than a warning.
The `String` mapping is also the reason the type of your data matters. The same string value will be typed into a text field, chosen in a radio group or selected in a dropdown, and which one happens is decided by the form, not by the gem.
The gaps are all about forms that are not plain
The rest of the limitations are specific enough to plan around, and most of them come from the same design decision: the gem finds fields by label.
Formulaic is currently tied to `simple_form` translations and field structure. If you pass a string for the attribute, we'll try to fill the input that relates to that label. The README adds that the authors would be happy to work with you to add support for other form builders, which is an invitation rather than a plan, and it tells you what the support would have to be built on.
Formulaic currently does not support forms with duplicate labels, as it is designed to be as similar as possible to a user completing a form, it looks at the labels to determine where to fill what data. A form with two fields both labelled Name is genuinely ambiguous to a person too, but a person sees the position and a test does not.
Formulaic can't figure out how to fill fields with HTML labels. `page.fill_in('<strong>Text</strong> here', with: 'something')` doesn't work with Capybara, and the usual workaround is to pass a CSS selector, which you can do by passing a string as the attribute key.
Formulaic can't handle multiple file attachments on the same input, which is a Capybara constraint rather than a Formulaic one but lands on the same person.
And then there is the AJAX assumption. Formulaic assumes your forms don't use AJAX, setting the wait time to 0. This can be configured using:
Formulaic.default_wait_time = 5A wait time of zero means the gem does not wait for the page to settle after filling a field, which is fine for a classic form and wrong for one that posts in the background. Setting it to five slows every interaction, so the right value depends on whether your forms are AJAX or not, and the README does not describe a per-form override.
A small gem that stopped releasing years ago
The maintenance picture is the part to weigh before adopting this.
The recent releases are v0.4.0 from 2017-02-06, v0.2.0 from 2015-02-06 and v0.1.4 from 2015-02-06. That is the last tagged release nine years before the date of this review, and the gap is not a quiet patch line, it is a project that released twice in three days in February 2015 and then once more in February 2017.
The repository itself is not abandoned. The last push was on 2026-07-15, the default branch is main, and the tree has the usual parts of a well-kept Ruby gem, with a gemspec, a Gemfile, a Rakefile, a lib directory, a spec directory, a .ruby-version file and a .ruby-gemset. The build badges are for Travis CI and Code Climate, both of which are older services, which dates the tooling as much as the releases.
The About section says Formulaic is maintained by Caleb Hearth and formerly thoughtbot with the help of community contributors, which is the credit line for a library that came out of a consultancy and has been carried on by its author.
So the honest summary is that this is a small, focused, well-documented gem whose last release predates most of the Ruby and Rails versions in use today, with a maintained repository but no recent packaging. Whether that matters depends on your Rails version and how much you depend on the translation conventions it assumes. If your app is on SimpleForm with I18n labels, the library itself is a handful of methods and the risk is mostly about the gem loading on your stack, not about the code being wrong. If your app has drifted to another form builder, the README's own admission is that you are on your own.
Editorial conclusion
Formulaic fits a Rails team with SimpleForm and I18n translations already in place whose feature specs spend more lines clicking fields than asserting behaviour, and who want the specification to read like the data the form holds. It is the wrong choice for a project on a different form builder, since the README says it is tied to simple_form translations and field structure, and the wrong choice for a form with duplicate labels or AJAX behaviour, both of which it explicitly does not support. Verify first that your labels match the simple_form.labels.<model>.<attribute> convention, that your submit buttons match helpers.submit.<model>.<action>, and that the gem still loads on your Ruby and Rails versions, since the last tagged release is v0.4.0 from 2017.
Frequently asked questions
What does the Formulaic gem do?
It removes the tedium of form filling with Capybara by letting you specify a hash of attributes to be input, rather than procedurally calling Capybara's DSL methods. It finds the right fields by looking at the form's labels, the way a person filling it in would.
What does Formulaic need from my application?
It relies on translations for SimpleForm and input helpers, using the simple_form.labels.<model>.<attribute> and helpers.submit.<model>.<action> conventions. You can still use it with strings as attribute keys, which it passes straight to fill_in, but then you have to find the submit buttons yourself.
How do I use Formulaic with RSpec?
Include the module in spec/spec_helper.rb with config.include Formulaic::Dsl, type: :feature. For Minitest or Test::Unit you include Capybara::DSL and Formulaic::Dsl in your ActionDispatch::IntegrationTest class, and for Capybara::TestHelper you either include Formulaic::Dsl in the base helper or delegate the four methods with delegate_to_test_context.
Which value types does Formulaic know how to fill in?
A String maps to fill_in, choose or select; a Date or ActiveSupport::TimeWithZone selects year, month and day; a TrueClass checks and a FalseClass unchecks; an Array checks or selects each member, which should all be strings, and errors if not all can be selected; and a File is attached with File#path.
What are the known limitations of Formulaic?
It is tied to simple_form translations and field structure, does not support forms with duplicate labels, cannot fill fields whose label contains HTML, cannot handle multiple file attachments on the same input, and assumes forms do not use AJAX, with the wait time set to 0 unless you change Formulaic.default_wait_time.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/calebhearth-formulaic)Was this useful?