วิธีทดสอบข้อกังวลใน Rails


105

เนื่องจากฉันมีPersonableข้อกังวลในแอปพลิเคชัน Rails 4 ของฉันซึ่งมีไฟล์full_nameวิธีการฉันจะทดสอบสิ่งนี้โดยใช้ RSpec ได้อย่างไร

ข้อกังวล / personable.rb

module Personable
  extend ActiveSupport::Concern

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

คุณใช้กรอบการทดสอบใด โปรดจำไว้ว่า Personable เป็นเพียงโมดูล Ruby ธรรมดา ทดสอบเหมือนกับที่คุณทดสอบมิกซ์อินอื่น ๆ
Lee Jarvis

ไม่ได้ActiveSupport::Concernถูกนำออกจาก Rails? ฉันคิดว่ามันผ่านไปสักครู่แล้ว
Russell

@LeeJarvis ฉันใช้ Rspec ร่วมกับ FactoryGirl
Kyle Decot


4
@ รัสเซลฉันเห็นด้วย ที่กล่าวว่าฉันจะไม่ช่วยใครบางคนในคำถามของพวกเขาเพียงเพราะพวกเขาทำตามวิธี Rails-y ในการทำบางสิ่งที่ฉันไม่เห็นด้วยกับมัน อย่างไรก็ตามนี่ก็เป็นการหลีกหนีหัวข้อของคำถามนี้ :-)
Lee Jarvis

คำตอบ:


176

วิธีการที่คุณพบจะใช้ทดสอบฟังก์ชันการทำงานได้เล็กน้อย แต่ดูเหมือนจะค่อนข้างบอบบาง - คลาสดัมมี่ของคุณ (อันที่จริงเป็นเพียงStructวิธีการแก้ปัญหาของคุณ) อาจมีพฤติกรรมเหมือนคลาสจริงที่includeคุณกังวลหรือไม่ก็ได้ นอกจากนี้หากคุณกำลังพยายามทดสอบความกังวลเกี่ยวกับโมเดลคุณจะไม่สามารถทำสิ่งต่างๆเช่นทดสอบความถูกต้องของวัตถุหรือเรียกใช้การเรียกกลับ ActiveRecord เว้นแต่คุณจะตั้งค่าฐานข้อมูลตามนั้น (เนื่องจากคลาสจำลองของคุณจะไม่มีการสำรองตารางฐานข้อมูล มัน). ยิ่งไปกว่านั้นคุณไม่เพียงต้องการทดสอบข้อกังวลเท่านั้น แต่ยังต้องทดสอบพฤติกรรมของข้อกังวลภายในข้อกำหนดรุ่นของคุณด้วย

ทำไมไม่ฆ่านกสองตัวด้วยหินก้อนเดียว? ด้วยการใช้กลุ่มตัวอย่างที่ใช้ร่วมกันของ RSpec คุณสามารถทดสอบข้อกังวลของคุณกับชั้นเรียนจริงที่ใช้พวกเขา (เช่นแบบจำลอง) และคุณจะสามารถทดสอบได้ทุกที่ที่ใช้ และคุณต้องเขียนการทดสอบเพียงครั้งเดียวจากนั้นรวมไว้ในข้อมูลจำเพาะของรุ่นใด ๆ ที่ตรงกับความกังวลของคุณ ในกรณีของคุณสิ่งนี้อาจมีลักษณะดังนี้:

# 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

ข้อดีของวิธีนี้จะชัดเจนยิ่งขึ้นเมื่อคุณเริ่มทำสิ่งที่คุณกังวลเช่นเรียกใช้การเรียกกลับ AR ซึ่งสิ่งที่น้อยกว่าวัตถุ AR จะไม่ทำ


2
parallel_testsข้อเสียอย่างหนึ่งของเรื่องนี้ก็คือว่ามันจะชะลอตัวลง ฉันคิดว่ามันจะดีกว่าที่จะมีการทดสอบแยกต่างหากแทนการใช้และshared_examples_for it_behaves_like
Artem Kalinchuk

6
@ArtemKalinchuk ฉันไม่แน่ใจว่าเป็นความจริงตามgithub.com/grosser/parallel_tests/issues/168 parallel_testsเป็นไปตามไฟล์ดังนั้นตัวอย่างที่แชร์ไม่ควรทำให้ช้าลง ฉันจะโต้แย้งว่าพฤติกรรมที่ใช้ร่วมกันที่จัดกลุ่มอย่างถูกต้องมีผลเหนือกว่าความเร็วในการทดสอบ
Aaron K

8
อย่าลืมรวมconcernsไดเร็กทอรีไว้ในspec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy

1
ฉันไม่พบสิ่งใดเกี่ยวกับการรวมไดเรกทอรีข้อกังวลในลิงก์นั้น คุณช่วยชี้แจงวิธีการนี้ได้ไหม ฉันไม่สามารถรับการทดสอบ RSpec เพื่อรับรู้โมดูลในข้อกังวลของฉัน
Jake Smith

4
ไม่ต้องเพิ่ม_specชื่อไฟล์ที่มี shared_examples_for (personable_spec.rb ในกรณีนี้) มิฉะนั้นคุณจะได้รับข้อความแจ้งเตือนที่ทำให้เข้าใจผิด - github.com/rspec/rspec-core/issues/828
Lalu

64

ในการตอบกลับความคิดเห็นที่ฉันได้รับนี่คือสิ่งที่ฉันได้ทำลงไป(หากใครมีการปรับปรุงโปรดโพสต์ได้ตามต้องการ) :

ข้อมูลจำเพาะ / ข้อกังวล / 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

1
ใช่นี้จะแบ่งการทดสอบอื่น ๆ Personถ้าพวกเขาเกิดขึ้นในการทดสอบระดับจริงที่เรียกว่า ฉันจะแก้ไขเพื่อแก้ไข
Russell

วิธีนี้ใช้ไม่ได้ มันทำให้ฉันมีข้อผิดพลาด:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot

ลองใส่ Personable แทนการขยาย ฉันจะอัปเดตคำตอบ
Russell

ใช้งานได้ดีในขณะนี้ ขอบคุณที่ชี้ให้ฉันไปในทิศทางที่ถูกต้องและช่วยฉัน refactor
@Russell

ใช้งานได้ดีและดูดี
Edward

7

ความคิดอีกประการหนึ่งคือการใช้อัญมณี with_modelเพื่อทดสอบสิ่งต่างๆเช่นนี้ ผมกำลังมองหาที่จะทดสอบความกังวลตัวเองและได้เห็นอัญมณี pg_search ทำเช่นนี้ ดูเหมือนว่าจะดีกว่าการทดสอบในแต่ละรุ่นเนื่องจากอาจมีการเปลี่ยนแปลงและเป็นการดีที่จะกำหนดสิ่งที่คุณต้องการในข้อมูลจำเพาะของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.