We make extensive use of ViewComponents in our Rails app. If you haven’t come across this new addition to the Rails toolkit, it takes some ideas from React and brings them to the rails party. It’s worth checking out. It has allowed us to rapidly build new interfaces and try out new ideas as we expand our product.
With the recommended way of testing, you need to add test attributes to your code, which can make the act of writing the test quite involved. We wanted to streamline this by taking inspiration from tools in the JavaScript world like jest snapshots and implement our own version of snapshot testing in RSpec. This type of testing is great for checking that you didn’t accidentally change something you didn’t mean to when adding a new feature or option to a component. The main drawback is since you are not asserting anything, you need to be diligent when creating snapshots!
We already use rspec-snapshot gem elsewhere in our application to test generated reports, so it was just a matter of comparing HTML instead of CSV files! Here’s a quick tip on how we do it.
RSpec Helper
In the helper below, we use the after hook only target component type tests where snapshotting is enabled. We then use the metadata from the test case to compose the filename.
If we detect that nothing has been assigned to rendered_component, we raise an error so the test fails. Otherwise, we use rspec-snapshot’s matcher match_snapshot to test everything was as expected.
Our test then looks like this:
RSpec.describe Table::SortHeaderComponent, type: :component do it "renders correctly without order", :snapshot do render_inline(described_class.new({ label: "header", header_key: 'key', sort_key: 'key', })) end it "renders correctly with ascending order", :snapshot do render_inline(described_class.new({ order: :asc, label: "asc header", header_key: 'key', sort_key: 'key', })) end it "renders correctly with descending order", :snapshot do render_inline(described_class.new({ order: :desc, label: "desc header", header_key: 'key', sort_key: 'key', })) end it "renders correctly with a different sort key", :snapshot do render_inline(described_class.new({ order: :desc, label: "desc header", header_key: 'key', sort_key: 'other_key', })) end end
On the first run, this ends up creating a file structure and snapshots to compare against in the future:
├── __snapshots__ │ └── table │ └── sort_header_component │ ├── _renders_correctly_with_a_different_sort_key.snap │ ├── _renders_correctly_with_ascending_order.snap │ ├── _renders_correctly_with_descending_order.snap │ └── _renders_correctly_without_order.snap ├── sort_header_component_spec.rb
Now if we want to update our snapshots we can just run the following command.
Otherwise, it works just as the rest of our RSpec testing does. No doubt in the future someone will wrap this all up in a nice gem, but until then this quick solution is working well. Hope this is interesting to anyone out there exploring view components!