Rails 보기 테스트를 작성하는 이유

17916 단어 railstestingruby


사진 제공: Nuno Silva on Unsplash

Ruby on Rails 애플리케이션을 테스트하는 방법에는 여러 가지가 있지만 자주 논의되지 않는 한 가지 방법이 있습니다. 이것이 Rails 보기를 테스트하는 방법입니다. 예 - 컨트롤러, 모델 및 기타 유형의 테스트가 있지만 뷰 레이어 테스트는 거의 볼 수 없습니다. 그들에게 더 많은 관심을 기울이고 그들이 무엇에 관한 것인지 봅시다.

통합 테스트와 테스트 뷰 레이어를 함께 작성하지 않는 이유는 무엇입니까? 할 수는 있지만 통합 테스트를 실행하는 것은 느릴 수 있으며 간단한 보기 '단위' 테스트를 작성하는 것이 더 간단할 수 있습니다. 또한 RSpec은 뷰 사양 작성을 훌륭하게 지원합니다. 더 흥미로운 점은 이것들을 테스트하기 위해 an example project을 만들었다는 것입니다. 내가 무엇을 알아냈는지 보자.

새 프로젝트, Who This

I created new Rails 6.1 project, installed RSpec, and generated the Book model with:

bin/rails generate scaffold Book title:string description:text download_url:string status:string

And look at what I got generated:

...

    create spec/views/books/edit.html.erb_spec.rb
    create spec/views/books/index.html.erb_spec.rb
    create spec/views/books/new.html.erb_spec.rb
    create spec/views/books/show.html.erb_spec.rb

...

If we take a look at one of the specs, we can find the following code:

# spec/views/books/index.html.erb_spec.rb

require 'rails_helper'

RSpec.describe "books/index", type: :view do
  before(:each) do
    assign(:books, [
      Book.create!(
        title: "Title",
        description: "MyText",
        download_url: "Download Url",
        status: "Status"
      ),
      Book.create!(
        title: "Title",
        description: "MyText",
        download_url: "Download Url",
        status: "Status"
      )
    ])
  end

  it "renders a list of books" do
    render
    assert_select "tr>td", text: "Title".to_s, count: 2
    assert_select "tr>td", text: "MyText".to_s, count: 2
    assert_select "tr>td", text: "Download Url".to_s, count: 2
    assert_select "tr>td", text: "Status".to_s, count: 2
  end
end

There’s the type: view that indicates the special type of specs. We will go into this later a bit. You can distinguish assign and render methods that indicate that they are defined internally and not something we should provide. But, there is also one thing sticking into my eyes as I go through this test.

What’s interesting here, this assert_select matcher looks a bit “deprecated” or like it is not from the RSpec world. There’s no class expect(...).to formation. What happened here is that the template for generating these specs got a bit dusty. It didn’t change from 2010, when it was originally pushed to the repo. You can find the commit that brought the assert_select here on GitHub .

걱정 마세요. 모델을 생성하고 생성된 뷰 사양에 의존하는 것은 고사하고 뷰 사양을 사용하는 많은 프로젝트를 보지 못했습니다. 그렇기 때문에 아무도 기존 템플릿을 리팩토링하거나 개선하는 데 시간을 할애하지 않은 것 같습니다. 하지만 이 블로그 게시물은 Rails 뷰 테스트에 집중하는 것에 관한 것이므로 그렇게 해보도록 하겠습니다.

봄맞이 대청소

If we take a look at the docs for view specs in RSpec 거의 모든 사용자가 다음을 사용하는 것을 볼 수 있습니다.

expect(rendered).to match /something/


RSpec에서 matchinclude를 사용할 수 있습니다. 우리가 얻는 것은 다음과 같은 테스트입니다.

# spec/views/books/index.html.erb_spec.rb

require 'rails_helper'

RSpec.describe "books/index", type: :view do
  before(:each) do
    assign(:books, [
      Book.create!(
        title: "Rails Testing",
        description: "How to test Ruby on Rails applications.",
        download_url: nil,
        status: "draft"
      ),
      Book.create!(
        title: "Rails Patterns",
        description: "A book about patterns and anti-patterns in Ruby on Rails.",
        download_url: "rails-patterns.com/download",
        status: "published"
      )
    ])
  end

  it "renders a list of books" do
    render

    expect(rendered).to match(/Rails Testing/)
    expect(rendered).to include("Rails Patterns")

    expect(rendered).to match(/How to test Ruby on Rails applications./)
    expect(rendered).to include("A book about patterns and anti-patterns in Ruby on Rails.")

    expect(rendered).to include("rails-patterns.com/download")

    expect(rendered).to include("published")
  end
end


이전 테스트는 RSpec 사양처럼 느껴집니다. 그러나 우리는 실제 콘텐츠가 특정 HTML 태그 안에 있는지 여부를 확인하는 기능을 잃었다는 것을 알 수 있습니다. assert_select는 예상 결과를 일치시키는 데 더 많은 유연성을 제공합니다. its docs 에서 assert_select 로 전달할 수 있는 옵션이 더 있습니다. 더 많은 제어 기능을 제공한다고 생각되는 옵션을 선택하는 것이 좋습니다.

캐피바라 활용

If you have Capybara installed, you can utilize its selectors like so:

require "rails_helper"

RSpec.describe "books/index", type: :view do
  before(:each) do
    assign(:books, [
      Book.create!(
        title: "Rails Testing",
        description: "How to test Ruby on Rails applications.",
        download_url: nil,
        status: "draft"
      ),
      Book.create!(
        title: "Rails Patterns",
        description: "A book about patterns and anti-patterns in Ruby on Rails.",
        download_url: "rails-patterns.com/download",
        status: "published"
      )
    ])
  end

  it "renders a list of books" do
    render

    expect(rendered).to have_selector("tr>td", text: "Rails Testing")
    expect(rendered).to have_selector("tr>td", text: "Rails Patterns")

    expect(rendered).to have_selector("tr>td", text: "How to test Ruby on Rails applications")
    expect(rendered).to have_selector("tr>td", text: "A book about patterns and anti-patterns in Ruby on Rails.")

    expect(rendered).to have_selector("tr>td", text: "rails-patterns.com/download")

    expect(rendered).to have_selector("tr>td", text: "published")
  end
end
Now, you get both RSpec expect(...).to , and you get the granularity of asserting that text is inside a table row. You can find all of the code and examples in the repo here . 그런데 왜 이것들 중 하나를 사용하겠습니까? 아래에서 논의합시다.

사양을 확인해야 하는 이유

We skimmed over a couple of reasons why you would write a view spec. The idea is to test some conditional logic you have in your views or partials. Writing an integration test that covers all the branches inside your views can be slow to run and painful to write. The view specs bring a great balance between:

  • 💸 cost of development,
  • 🏍 speed of execution, and
  • 🔀 conditional rendering coverage.

Of course, you might not need view specs at all if you have decorators and view models, form objects, and all other goodies that can move the logic out of the view for you. But, sometimes, in the real world, not every code base is perfectly designed, and you have to cut corners from time to time.

Whether it is some kind of stakeholder breathing down your neck. Or it’s the complicated legacy partial that can’t be so easily extracted to a design of your choice. Whatever the reason is, you might opt-in for the view spec to move fast and have the logic tested.

And when that day comes (or it already came), you can resort back to this blog post and use it to your liking.

If you liked the post, . Consider subscribing to the to get new articles like this one.

Catch you in the next one, cheers.

좋은 웹페이지 즐겨찾기