Cara Menguji Masalah di Rails

104

Mengingat bahwa saya memiliki Personablemasalah dengan aplikasi Rails 4 saya yang memiliki full_namemetode, bagaimana saya akan menguji ini menggunakan RSpec?

kekhawatiran / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
Kyle Decot
sumber
Kerangka pengujian apa yang Anda gunakan? Juga ingat Personable hanyalah modul Ruby biasa. Ujilah seperti Anda menguji mixin lainnya.
Lee Jarvis
Belum ActiveSupport::Concerndibawa keluar dari Rails? Saya pikir itu pergi beberapa waktu yang lalu.
Russell
@LeeJarvis Saya menggunakan Rspec bersama dengan FactoryGirl
Kyle Decot
4
@ Russell Saya setuju. Meskipun demikian, saya tidak akan membantu seseorang dengan pertanyaan mereka hanya karena mereka mengikuti cara Rails-y dalam melakukan sesuatu yang saya tidak setuju dengannya. Bagaimanapun, ini agak keluar dari subjek pertanyaan ini :-)
Lee Jarvis

Jawaban:

176

Metode yang Anda temukan pasti akan berfungsi untuk menguji sedikit fungsionalitas tetapi tampaknya cukup rapuh — kelas dummy Anda (sebenarnya hanya Structdalam solusi Anda) mungkin atau mungkin tidak berperilaku seperti kelas nyata yanginclude menjadi perhatian Anda. Selain itu jika Anda mencoba untuk menguji masalah model, Anda tidak akan dapat melakukan hal-hal seperti menguji validitas objek atau memanggil callback ActiveRecord kecuali Anda mengatur database yang sesuai (karena kelas dummy Anda tidak akan memiliki dukungan tabel database Itu). Selain itu, Anda tidak hanya ingin menguji kekhawatiran tetapi juga menguji perilaku kekhawatiran di dalam spesifikasi model Anda.

Jadi mengapa tidak membunuh dua burung dengan satu batu? Dengan menggunakan grup contoh bersama RSpec , Anda dapat menguji kekhawatiran Anda terhadap kelas sebenarnya yang menggunakannya (misalnya, model) dan Anda akan dapat mengujinya di mana pun mereka digunakan. Dan Anda hanya perlu menulis pengujian sekali dan kemudian memasukkannya ke dalam spesifikasi model yang menggunakan perhatian Anda. Dalam kasus Anda, ini mungkin terlihat seperti ini:

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

Keuntungan dari pendekatan ini menjadi lebih jelas ketika Anda mulai melakukan hal-hal yang menjadi perhatian Anda seperti memanggil callback AR, di mana apa pun yang kurang dari objek AR tidak akan berhasil.

Josh Leitzel
sumber
2
Salah satu kelemahannya adalah ia akan melambat parallel_tests. Saya pikir akan lebih baik untuk memiliki tes terpisah daripada menggunakan shared_examples_fordan it_behaves_like.
Artem Kalinchuk
6
@ArtemKalinchuk Saya tidak yakin itu benar, per github.com/grosser/parallel_tests/issues/168 parallel_tests didasarkan pada per file, jadi contoh yang dibagikan seharusnya tidak memperlambatnya. Saya juga berpendapat bahwa perilaku bersama yang dikelompokkan dengan benar, mengalahkan kecepatan pengujian.
Aaron K
8
Pastikan untuk memasukkan concernsdirektori di spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy
1
Saya tidak dapat menemukan apa pun tentang menyertakan direktori kekhawatiran di tautan itu. Bisakah Anda menjelaskan bagaimana ini dilakukan? Saya tidak bisa mendapatkan tes RSpec saya untuk mengenali modul di salah satu masalah saya.
Jake Smith
4
Jangan menambahkan _specnama file yang berisi shared_examples_for (personable_spec.rb dalam kasus ini), jika tidak, Anda akan mendapatkan pesan peringatan yang menyesatkan - github.com/rspec/rspec-core/issues/828 .
Lalu
62

Menanggapi komentar yang saya terima, inilah yang akhirnya saya lakukan (jika ada yang memiliki peningkatan, silakan mempostingnya) :

spec / concern / personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end
Kyle Decot
sumber
1
Ya, ini akan menghentikan tes lain jika mereka kebetulan menguji kelas nyata yang disebut Person. Saya akan mengedit untuk memperbaikinya.
Russell
Ini tidak berhasil. Ini memberi saya kesalahan:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot
Coba sertakan Personable daripada memperpanjangnya. Saya akan memperbarui jawabannya.
Russell
Bekerja dengan baik sekarang. Terima kasih telah mengarahkan saya ke arah yang benar dan membantu saya merefaktor ulang @Russell
Kyle Decot
Berfungsi bagus dan terlihat bagus
Edward
7

Pemikiran lain adalah menggunakan permata with_model untuk menguji hal-hal seperti ini. Saya sendiri sedang mencoba menguji kekhawatiran dan telah melihat permata pg_search melakukan ini . Tampaknya jauh lebih baik daripada menguji pada model individual, karena itu mungkin berubah, dan bagus untuk menentukan hal-hal yang Anda perlukan dalam spesifikasi Anda.

lobati
sumber