0
\$\begingroup\$

I am working with: ruby 3.0.0p0 and Rails 7.0.4 and rspec-rails (6.0.1)

I want to test my views. I created Product model with rails scaffolding. I tested all views but I am not sure about _form.html.erb testing. Because I have two status for this view. One of them is New page and other is Edit page. If I go to Edit page the fields have value but I go to New page the fields have no value.

views/products/_form.html.erb

<%= form_with(model: product, class: "contents") do |form| %>
  <% if product.errors.any? %>
    <div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
      <h2><%= pluralize(product.errors.count, "error") %> prohibited this product from being saved:</h2>

      <ul>
        <% product.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="my-5">
    <%= form.label :title %>
    <%= form.text_field :title, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
  </div>

  <div class="my-5">
    <%= form.label :description %>
    <%= form.text_area :description, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
  </div>

  <div class="my-5">
    <%= form.label :image_url %>
    <%= form.text_field :image_url, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
  </div>

  <div class="my-5">
    <%= form.label :price %>
    <%= form.text_field :price, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
  </div>

  <div class="inline">
    <%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
  </div>
<% end %>

So I used Nokogiri for catching inputs value. But I am not sure about that and I couldn't best practice for that.

This is my code:

spec/views/products/_form.html.erb_spec.rb

require "rails_helper"

RSpec.describe "rendering locals in a partial", type: :view do
  subject { Nokogiri::HTML(rendered) }

  it "form for editing the product" do
    product = Product.create!(title: "dicer", description: "Hello World 2", price: 10.1)

    render :partial => "products/form", :locals => { :product => product }

    aggregate_failures do
      expect(subject.at('input[@name="product[title]"]')['value']).to eq(product.title)
      expect(subject.at('textarea[@name="product[description]"]').text.strip).to eq(product.description)
      expect(subject.at('input[@name="product[image_url]"]')['value']).to eq(product.image_url)
      expect(subject.at('input[@name="product[price]"]')['value'].to_f).to eq(product.price.to_f)
      expect(rendered).to have_button('Update Product')
    end
  end

  it "form for creating a product" do
    product = Product.new

    render :partial => "products/form", :locals => { :product => product }

    aggregate_failures do
      expect(subject.at('input[@name="product[title]"]')['value']).to eq(nil)
      expect(subject.at('textarea[@name="product[description]"]').text.strip).to eq("")
      expect(subject.at('input[@name="product[image_url]"]')['value']).to eq(nil)
      expect(subject.at('input[@name="product[price]"]')['value']).to eq(nil)
      expect(rendered).to have_button('Create Product')
    end
  end
end
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

The real question here is not if this is good implementation but what value these tests provide against regression.

For example when you're testing:

expect(subject.at('input[@name="product[title]"]')['value']).to eq(nil)

That doesn't actually tell you anything about the behavior of the application since the test would still pass if you didn't even pass a model to the form builder!

Similarly when you're testing the update scenario you're not covering the one thing that actually matters - if the correct record is passed to the view in the first place. Instead you're just "testing the test" and the test is strongly coupled to the implementation of the form.

There are legitimate uses for view specs, but testing partials is very much like testing a private method. You're testing the implementation and not the actual behavior.

Instead consider writing system specs which drive the entire stack - filling out the form and submitting it as a far better way to determine if the application is working as intended.

\$\endgroup\$
1
\$\begingroup\$

That seems reasonable, but I'm not sure if you have a specific concern. You are testing what the form looks like for a new product, so that's right. This form and code may evolve as the software evolves, and this gives you a good place to test for new behaviors.

There's not lots of logic being tested here, but you're covering it. I see one missing a case: the product with validation errors case. You'd want to assert that the errors appear as you expect them. So, if you're going to have these view tests, I'd recommend you add tests for this, as that's typically something that goes wrong with forms.

\$\endgroup\$
1
  • \$\begingroup\$ thanks for your answer. I'gonna add validation errors case. \$\endgroup\$ Commented Jan 10, 2023 at 7:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.