เมื่อใดที่จะใช้ RSpec let ()


448

ฉันมักจะใช้ก่อนบล็อกเพื่อตั้งค่าตัวแปรอินสแตนซ์ จากนั้นฉันก็ใช้ตัวแปรเหล่านั้นกับตัวอย่างของฉัน let()ฉันเพิ่งมาถึง ตามเอกสาร RSpec มันถูกใช้เพื่อ

... เพื่อกำหนดวิธีใช้ตัวช่วยจำ ค่าจะถูกแคชระหว่างการโทรหลายครั้งในตัวอย่างเดียวกัน แต่จะไม่ข้ามตัวอย่าง

สิ่งนี้แตกต่างจากการใช้ตัวแปรอินสแตนซ์ในบล็อกก่อนหน้าอย่างไร และเมื่อใดที่คุณควรใช้let()vs before()?


1
ปล่อยให้บล็อกถูกประเมินอย่างเกียจคร้านขณะที่ก่อนที่บล็อกจะรันก่อนแต่ละตัวอย่าง (โดยรวมจะช้ากว่า) การใช้บล็อกก่อนขึ้นอยู่กับความชอบส่วนบุคคล (สไตล์การเข้ารหัส, mocks / stubs ... ) โดยปกติแล้วให้บล็อกเป็นที่ต้องการ คุณสามารถตรวจสอบข้อมูลรายละเอียดเพิ่มเติมเกี่ยวกับการปล่อยให้
Nesha Zoric

ไม่ใช่แนวปฏิบัติที่ดีในการตั้งค่าตัวแปรอินสแตนซ์ในเบ็ดก่อน ตรวจสอบbetterspecs.org
อัลลิสัน

คำตอบ:


605

ฉันมักจะชอบletตัวแปรอินสแตนซ์ด้วยเหตุผลสองประการ:

  • ตัวแปรอินสแตนซ์จะปรากฏขึ้นเมื่อมีการอ้างอิง ซึ่งหมายความว่าหากคุณอ้วนการสะกดของตัวแปรอินสแตนซ์จะสร้างและเริ่มต้นnilใหม่ซึ่งจะนำไปสู่ข้อบกพร่องที่ลึกซึ้งและบวกเท็จ ตั้งแต่letสร้างวิธีการคุณจะได้รับNameErrorเมื่อคุณสะกดผิดซึ่งฉันคิดว่าดีกว่า ทำให้ง่ายต่อการกำหนดรายละเอียดอีกด้วย
  • before(:each)เบ็ดจะทำงานก่อนแต่ละตัวอย่างเช่นแม้ว่าตัวอย่างเช่นไม่ใช้ใด ๆ ของตัวแปรเช่นที่กำหนดไว้ในเบ็ด นี่ไม่ใช่เรื่องใหญ่ แต่ถ้าการตั้งค่าตัวแปรอินสแตนซ์ใช้เวลานานคุณจะเสียรอบ สำหรับวิธีการที่กำหนดโดยletรหัสการเริ่มต้นทำงานเฉพาะถ้าตัวอย่างเรียกมันว่า
  • คุณสามารถ refactor จากตัวแปรโลคัลในตัวอย่างโดยตรงเป็น let โดยไม่เปลี่ยนไวยากรณ์การอ้างอิงในตัวอย่าง หากคุณ refactor เป็นตัวแปรอินสแตนซ์คุณต้องเปลี่ยนวิธีการอ้างอิงวัตถุในตัวอย่าง (เช่นเพิ่ม an @)
  • นี่เป็นเรื่องส่วนตัว แต่เมื่อ Mike Lewis ชี้ให้เห็นฉันคิดว่ามันทำให้ข้อมูลจำเพาะง่ายต่อการอ่าน ฉันชอบการจัดระเบียบของวัตถุที่อ้างถึงทั้งหมดของฉันด้วยletและทำให้itบล็อกของฉันดีและสั้น

ลิงค์ที่เกี่ยวข้องสามารถพบได้ที่นี่: http://www.betterspecs.org/#let


2
ฉันชอบข้อได้เปรียบแรกที่คุณพูดถึง แต่คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับข้อสามได้ไหม จนถึงตอนนี้ฉันได้เห็นแล้ว (รายละเอียด mongoid: github.com/mongoid/mongoid/blob/master/spec/functional/mongoid/ ) ใช้บล็อกบรรทัดเดียวและฉันไม่เห็นว่าไม่มี "@" ทำให้ อ่านง่ายขึ้น
ส่ง

6
อย่างที่ฉันบอกว่ามันเป็นอัตนัยเล็กน้อย แต่ฉันคิดว่ามันมีประโยชน์ที่จะใช้letในการกำหนดวัตถุที่ต้องพึ่งพาทั้งหมดและเพื่อใช้before(:each)ในการตั้งค่าการกำหนดค่าที่ต้องการหรือ mocks / stubs ใด ๆ ที่ต้องการโดยตัวอย่าง ฉันชอบสิ่งนี้มากกว่าหนึ่งอันก่อนที่จะเกี่ยวทั้งหมดนี้ นอกจากนี้ยังlet(:foo) { Foo.new }มีเสียงดังน้อย (และอื่น ๆ ไปยังจุด) before(:each) { @foo = Foo.new }แล้ว นี่คือตัวอย่างของวิธีการใช้งานของฉัน: github.com/myronmarston/vcr/blob/v1.7.0/spec/vcr/util/ ......
Myron Marston

ขอบคุณสำหรับตัวอย่างที่ช่วยได้จริงๆ
ส่ง Hil

3
Andrew Grimm: จริง แต่คำเตือนอาจทำให้เกิดเสียงดังขึ้นมากมาย (เช่นจากอัญมณีที่คุณใช้ซึ่งไม่ได้ทำงานโดยไม่มีการเตือน) นอกจากนี้ฉันชอบที่NoMethodErrorจะได้รับการเตือน แต่ YMMV
Myron Marston

1
@ Jwan622: คุณอาจเริ่มต้นด้วยการเขียนตัวอย่างหนึ่งซึ่งมีfoo = Foo.new(...)แล้วผู้ใช้fooในบรรทัดต่อมา หลังจากนั้นคุณเขียนตัวอย่างใหม่ในกลุ่มตัวอย่างเดียวกันที่ต้องการFooอินสแตนซ์ในลักษณะเดียวกัน ณ จุดนี้คุณต้องการ refactor เพื่อกำจัดการทำซ้ำ คุณสามารถลบfoo = Foo.new(...)บรรทัดออกจากตัวอย่างของคุณและแทนที่ด้วยการlet(:foo) { Foo.new(...) }เปลี่ยนแปลงโดยfooไม่ใช้วิธีตัวอย่าง แต่ถ้าคุณจะ refactor before { @foo = Foo.new(...) }คุณยังมีการอ้างอิงการปรับปรุงในตัวอย่างจากไปfoo @foo
Myron Marston

82

ความแตกต่างระหว่างการใช้ตัวแปรอินสแตนซ์และlet()เป็นที่let()เป็นขี้เกียจประเมิน ซึ่งหมายความว่าlet()จะไม่มีการประเมินจนกว่าจะมีการเรียกใช้วิธีที่กำหนดเป็นครั้งแรก

ความแตกต่างระหว่างbeforeและletคือlet()ให้วิธีที่ดีในการกำหนดกลุ่มของตัวแปรในสไตล์ 'cascading' โดยการทำเช่นนี้ spec ดูดีขึ้นเล็กน้อยโดยการทำให้รหัสง่ายขึ้น


1
ฉันเห็นว่ามันเป็นข้อได้เปรียบจริง ๆ ? รหัสกำลังถูกเรียกใช้สำหรับแต่ละตัวอย่างโดยไม่คำนึงถึง
ส่ง

2
ง่ายต่อการอ่าน IMO และความสามารถในการอ่านเป็นปัจจัยสำคัญในภาษาโปรแกรม
Mike Lewis

4
Senthil - จริง ๆ แล้วมันไม่จำเป็นต้องทำงานในทุกตัวอย่างเมื่อคุณใช้ let () มันขี้เกียจดังนั้นมันจะทำงานก็ต่อเมื่อมีการอ้างอิง โดยทั่วไปการพูดแบบนี้ไม่สำคัญมากนักเนื่องจากจุดของกลุ่มตัวอย่างคือการให้ตัวอย่างหลายตัวอย่างทำงานในบริบททั่วไป
David Chelimsky

1
ดังนั้นหมายความว่าคุณไม่ควรใช้letถ้าคุณต้องการบางสิ่งที่จะประเมินทุกครั้งหรือไม่ เช่นฉันต้องการโมเดลเด็กที่จะนำเสนอในฐานข้อมูลก่อนที่พฤติกรรมบางอย่างจะถูกเรียกในรูปแบบผู้ปกครอง ฉันไม่จำเป็นต้องอ้างถึงแบบจำลองเด็กในการทดสอบเพราะฉันกำลังทดสอบพฤติกรรมของแบบจำลองผู้ปกครอง ในขณะที่ฉันใช้let!วิธีการแทน แต่อาจจะชัดเจนกว่าที่จะนำการตั้งค่านั้นมาใช้before(:each)?
gar

2
@gar - ฉันจะใช้ Factory (เช่น FactoryGirl) ซึ่งอนุญาตให้คุณสร้างการเชื่อมโยงเด็ก ๆ ที่ต้องการเมื่อคุณสร้างอินสแตนซ์ของผู้ปกครอง หากคุณทำเช่นนี้มันจะไม่สำคัญถ้าคุณใช้ let () หรือบล็อกการตั้งค่า let () นั้นดีถ้าคุณไม่ต้องการใช้ EVERYTHING สำหรับการทดสอบแต่ละครั้งในบริบทย่อยของคุณ การตั้งค่าควรมีเฉพาะสิ่งที่จำเป็นสำหรับแต่ละรายการ
Harmon

17

ฉันได้แทนที่การใช้ตัวแปรอินสแตนซ์ทั้งหมดในการทดสอบ rspec ของฉันทั้งหมดเพื่อใช้ let () ฉันได้เขียนตัวอย่าง quickie สำหรับเพื่อนที่ใช้เพื่อสอนคลาส Rspec ขนาดเล็ก: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html

ตามที่บางคำตอบอื่น ๆ ที่นี่บอกว่าให้ () ได้รับการประเมินอย่างขี้เกียจดังนั้นมันจะโหลดเฉพาะคำที่ต้องการโหลดเท่านั้น มันแห้งขึ้นข้อมูลจำเพาะและทำให้อ่านง่ายขึ้น ในความเป็นจริงฉันได้รับการถ่ายทอดรหัส Rspec let () เพื่อใช้ในตัวควบคุมของฉันในรูปแบบของอัญมณีสืบทอด http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html

นอกเหนือจากการประเมินแบบขี้เกียจข้อดีอื่น ๆ ก็คือเมื่อรวมกับ ActiveSupport :: Concern และข้อมูลจำเพาะ / การสนับสนุน / พฤติกรรมโหลดทุกอย่างคุณสามารถสร้างข้อมูลจำเพาะ mini-DSL เฉพาะสำหรับแอปพลิเคชันของคุณได้ ฉันได้เขียนรายการทดสอบเพื่อใช้กับ Rack และทรัพยากร RESTful

กลยุทธ์ที่ฉันใช้คือ Factory- ทุกอย่าง (ผ่าน Machinist + Forgery / Faker) อย่างไรก็ตามมันเป็นไปได้ที่จะใช้ร่วมกับบล็อกก่อน (: แต่ละรายการ) เพื่อโหลดโรงงานสำหรับกลุ่มตัวอย่างทั้งชุดทำให้สเป็คทำงานได้เร็วขึ้น: http://makandra.com/notes/770-taking-advantage -of-rspec-s-ให้ในก่อนที่จะบล็อก


เฮ้โฮ - Sheng จริง ๆ แล้วฉันได้อ่านบล็อกโพสต์ของคุณก่อนถามคำถาม เกี่ยวกับคุณ# spec/friendship_spec.rbและ# spec/comment_spec.rbตัวอย่างคุณไม่คิดว่าพวกเขาทำให้อ่านง่ายหรือไม่ ฉันไม่รู้ว่าusersมาจากไหนและจะต้องขุดลึกลงไปอีก
ส่ง Hil

ผู้คนนับโหลคนแรกที่ฉันได้แสดงรูปแบบให้ทุกคนพบว่าสามารถอ่านได้มากขึ้นและบางคนก็เริ่มเขียนด้วย ฉันมีสเป็คเพียงพอในขณะนี้โดยใช้ let () ที่ฉันพบปัญหาบางอย่างเช่นกัน ฉันพบว่าตัวเองกำลังจะไปที่ตัวอย่างและเริ่มต้นจากกลุ่มตัวอย่างที่อยู่ด้านในสุดทำงานตัวเองสำรอง มันเป็นทักษะเดียวกับการใช้สภาพแวดล้อมที่สามารถตั้งโปรแกรมได้สูง
Ho-Sheng Hsiao

2
gotcha ที่ใหญ่ที่สุดที่ฉันพบนั้นบังเอิญใช้ let (: subject) {} แทนที่จะเป็น subject {} subject () ถูกตั้งค่าแตกต่างจาก let (: subject) แต่ let (: subject) จะลบล้างมัน
Ho-Sheng Hsiao

1
หากคุณสามารถปล่อย "เจาะลึก" ลงในโค้ดคุณจะพบการสแกนโค้ดด้วยการประกาศ let () มากขึ้นเร็วขึ้นมาก เป็นการง่ายกว่าที่จะเลือกการประกาศ let () เมื่อสแกนโค้ดมากกว่าการค้นหา @variables ที่ฝังอยู่ในโค้ด การใช้ @variables ฉันไม่มี "รูปร่าง" ที่ดีสำหรับบรรทัดที่อ้างถึงการกำหนดให้กับตัวแปรและบรรทัดใดที่อ้างถึงการทดสอบตัวแปร การใช้ let () การมอบหมายทั้งหมดจะเสร็จสิ้นด้วย let () เพื่อให้คุณรู้ว่า "ทันที" โดยใช้ตัวอักษรที่เป็นประกาศของคุณ
Ho-Sheng Hsiao

1
คุณสามารถสร้างอาร์กิวเมนต์เดียวกันเกี่ยวกับตัวแปรอินสแตนซ์ได้ง่ายขึ้นโดยเฉพาะอย่างยิ่งเนื่องจากตัวแก้ไขบางตัวเช่น Mine (gedit) เน้นตัวแปรอินสแตนซ์ ฉันใช้let()สองสามวันที่ผ่านมาและโดยส่วนตัวฉันไม่เห็นความแตกต่างยกเว้นข้อได้เปรียบแรกที่ Myron พูดถึง และฉันก็ไม่แน่ใจเกี่ยวกับการปล่อยและสิ่งที่ไม่อาจเพราะฉันขี้เกียจและฉันชอบเห็นรหัสล่วงหน้าโดยไม่ต้องเปิดอีกไฟล์ ขอบคุณสำหรับความคิดเห็นของคุณ
ส่ง Hil

13

เป็นสิ่งสำคัญที่ต้องจำไว้ว่าการปล่อยให้ขี้เกียจประเมินและไม่ใส่วิธีผลข้างเคียงในนั้นมิฉะนั้นคุณจะไม่สามารถเปลี่ยนจากการปล่อยเป็นก่อน (: แต่ละรายการ)ได้อย่างง่ายดาย คุณสามารถใช้ให้! แทนที่จะปล่อยให้มันถูกประเมินก่อนแต่ละสถานการณ์


8

โดยทั่วไปแล้วlet()เป็นไวยากรณ์ที่ดีกว่าและจะช่วยให้คุณพิมพ์@nameสัญลักษณ์ทั่วสถานที่ แต่ข้อแม้ emptor! ฉันได้พบlet()ยังแนะนำข้อบกพร่องที่ลึกซึ้ง (หรืออย่างน้อยหัวเกา) เพราะตัวแปรไม่ได้มีอยู่จริงจนกว่าคุณจะพยายามที่จะใช้มัน ... Tell tale sign: หากเพิ่ม a putsหลังlet()เพื่อดูว่าตัวแปรนั้นถูกต้องจะช่วยให้สเป็ค ที่จะผ่าน แต่ไม่มีputsสเป็คล้มเหลว - คุณได้พบความละเอียดอ่อนนี้แล้ว

ฉันยังพบว่าlet()ดูเหมือนจะไม่แคชในทุกสถานการณ์! ฉันเขียนมันในบล็อกของฉัน: http://technicaldebt.com/?p=1242

อาจจะเป็นแค่ฉันเหรอ?


9
letจำค่าในช่วงเวลาของตัวอย่างเดียวเสมอ มันไม่ได้บันทึกค่าในตัวอย่างหลาย ๆ before(:all)ในทางตรงกันข้ามช่วยให้คุณสามารถใช้ตัวแปรเริ่มต้นในหลายตัวอย่าง
Myron Marston

2
หากคุณต้องการใช้ let (ตามที่ดูเหมือนว่าเป็นแนวปฏิบัติที่ดีที่สุด) แต่ต้องการตัวแปรเฉพาะเพื่อสร้างอินสแตนซ์ทันทีนั่นคือสิ่งที่let!ถูกออกแบบมา relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/ …
Jacob

6

ให้เป็นหน้าที่ของมันเป็นหลัก Proc ยังแคชของมัน

หนึ่ง gotcha ที่ฉันพบทันทีพร้อมกับปล่อยให้ ... ในบล็อก Spec ที่กำลังประเมินการเปลี่ยนแปลง

let(:object) {FactoryGirl.create :object}

expect {
  post :destroy, id: review.id
}.to change(Object, :count).by(-1)

คุณจะต้องแน่ใจว่าได้โทรletออกนอกบล็อกที่คาดไว้ นั่นคือคุณกำลังโทรFactoryGirl.createเข้าบล็อกการอนุญาตของคุณ ฉันมักจะทำสิ่งนี้โดยตรวจสอบว่าวัตถุนั้นยังคงอยู่

object.persisted?.should eq true

มิฉะนั้นเมื่อ letบล็อกถูกเรียกครั้งแรกการเปลี่ยนแปลงในฐานข้อมูลจะเกิดขึ้นจริงเนื่องจากการสร้างอินสแตนซ์ขี้เกียจ

ปรับปรุง

เพียงแค่เพิ่มบันทึก ระวังการเล่นกอล์ฟรหัสหรือในกรณีนี้ rspec golf พร้อมคำตอบนี้

ในกรณีนี้ฉันแค่ต้องเรียกวิธีการบางอย่างที่วัตถุตอบสนอง ดังนั้นฉันขอร้อง_.persisted?เมธอด _ บนวัตถุเป็นความจริง ทั้งหมดที่ฉันพยายามทำคือยกตัวอย่างวัตถุ คุณสามารถโทรว่างเปล่า หรือไม่มี เกินไป. ประเด็นไม่ใช่การทดสอบ แต่นำชีวิตของวัตถุโดยเรียกมันว่า

ดังนั้นคุณไม่สามารถ refactor

object.persisted?.should eq true

เป็น

object.should be_persisted 

ในขณะที่วัตถุไม่ได้ถูกยกตัวอย่าง ... มันขี้เกียจ :)

อัปเดต 2

ใช้ประโยชน์จากการปล่อย! ไวยากรณ์สำหรับการสร้างวัตถุทันทีซึ่งควรหลีกเลี่ยงปัญหานี้โดยสิ้นเชิง หมายเหตุถึงแม้ว่ามันจะเอาชนะจุดประสงค์มากมายของความเกียจคร้านของการไม่ยอมให้

ในบางกรณีคุณอาจต้องการใช้ประโยชน์จากไวยากรณ์หัวเรื่องแทนการปล่อยให้เป็นจริงเพราะมันอาจให้ตัวเลือกเพิ่มเติมแก่คุณ

subject(:object) {FactoryGirl.create :object}

2

หมายเหตุถึงโจเซฟ - ถ้าคุณกำลังสร้างวัตถุฐานข้อมูลใน before(:all)พวกเขาจะไม่ถูกบันทึกในการทำธุรกรรมและคุณมีแนวโน้มที่จะทิ้ง cruft ในฐานข้อมูลการทดสอบของคุณ ใช้before(:each)แทน

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

context "foo" do
  let(:params) do
     { :foo => foo,  :bar => "bar" }
  end
  let(:foo) { "foo" }
  it "is set to foo" do
    params[:foo].should eq("foo")
  end
  context "when foo is bar" do
    let(:foo) { "bar" }
    # NOTE we didn't have to redefine params entirely!
    it "is set to bar" do
      params[:foo].should eq("bar")
    end
  end
end

1
+1 ก่อนหน้า (: ทั้งหมด) ข้อบกพร่องใช้เวลาหลายวันในการพัฒนาของเรา
Michael Durrant

1

"ก่อน" before(:each)โดยหมายถึงการเริ่มต้น Ref The Rspec Book, ลิขสิทธิ์ 2010, หน้า 228

before(scope = :each, options={}, &block)

ฉันใช้before(:each)เพื่อเก็บข้อมูลบางอย่างสำหรับกลุ่มตัวอย่างแต่ละกลุ่มโดยไม่ต้องเรียกletวิธีการเพื่อสร้างข้อมูลในบล็อก "it" รหัสน้อยลงในบล็อก "มัน" ในกรณีนี้

ฉันใช้letถ้าฉันต้องการข้อมูลบางอย่างในบางตัวอย่าง แต่ไม่ใช่ข้อมูลอื่น

ทั้งก่อนหน้าและปล่อยให้ดีสำหรับ DRYing อัพบล็อก "มัน"

เพื่อหลีกเลี่ยงความสับสนใด ๆ "ให้" before(:all)ไม่ได้เช่นเดียวกับ "ให้" ประเมินวิธีและค่าของมันซ้ำอีกครั้งสำหรับแต่ละตัวอย่าง ("it") แต่เก็บค่าข้ามการโทรหลายสายในตัวอย่างเดียวกัน คุณสามารถอ่านเพิ่มเติมได้ที่นี่: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let


1

การแยกเสียงที่นี่: หลังจาก 5 ปีของ rspec ฉันไม่ชอบletมาก

1. การประเมิน Lazy มักทำให้การตั้งค่าการทดสอบสับสน

มันยากที่จะให้เหตุผลเกี่ยวกับการตั้งค่าเมื่อบางสิ่งที่ได้รับการประกาศในการตั้งค่าไม่ได้ส่งผลกระทบต่อสถานะจริงในขณะที่คนอื่น ๆ กำลัง

ในที่สุดก็ออกมาจากคนแห้วเพียงการเปลี่ยนแปลงletไปlet!(สิ่งเดียวกันโดยไม่ต้องประเมินผลขี้เกียจ) เพื่อให้ได้รับการทำงานของพวกเขาสเปค หากวิธีนี้ใช้ได้ผลสำหรับพวกเขานิสัยใหม่ก็จะเกิดขึ้น: เมื่อสเป็คใหม่ถูกเพิ่มเข้าไปในชุดที่เก่ากว่าและมันไม่ทำงานสิ่งแรกที่นักเขียนพยายามคือการเพิ่มการletโทรเข้าแบบสุ่ม

ไม่นานนักประสิทธิภาพการทำงานทั้งหมดจะหมดไป

2. ไวยากรณ์พิเศษผิดปกติสำหรับผู้ใช้ที่ไม่ใช่ rspec

ฉันอยากจะสอนทับทิมให้ทีมของฉันมากกว่าเทคนิคของ rspec ตัวแปรอินสแตนซ์หรือการเรียกเมธอดมีประโยชน์ทุกที่ในโครงการนี้และอื่น ๆletไวยากรณ์จะมีประโยชน์เฉพาะใน rspec

3. "ผลประโยชน์" ช่วยให้เราสามารถเพิกเฉยต่อการเปลี่ยนแปลงการออกแบบที่ดีได้อย่างง่ายดาย

let()ดีสำหรับการอ้างอิงที่มีราคาแพงซึ่งเราไม่ต้องการสร้างซ้ำแล้วซ้ำอีก อีกทั้งยังจับคู่กันได้ดีกับsubjectช่วยให้คุณสามารถเรียกซ้ำไปยังวิธีการหลายอาร์กิวเมนต์

การอ้างอิงที่มีราคาแพงซ้ำหลายครั้งและวิธีการที่มีลายเซ็นขนาดใหญ่เป็นจุดที่เราสามารถทำให้โค้ดดีขึ้น:

  • บางทีฉันสามารถแนะนำสิ่งที่เป็นนามธรรมใหม่ที่แยกการพึ่งพาจากส่วนที่เหลือของรหัสของฉัน (ซึ่งจะหมายถึงการทดสอบที่จำเป็นน้อยลง)
  • รหัสที่ทดสอบอาจทำมากเกินไป
  • บางทีฉันอาจต้องฉีดวัตถุที่ฉลาดกว่าแทนรายการดั้งเดิม
  • บางทีฉันอาจมีการละเมิดอย่าบอก
  • บางทีรหัสที่มีราคาแพงสามารถทำได้เร็วขึ้น (หายาก - ระวังการปรับให้เหมาะสมก่อนเวลาที่นี่)

ในทุกกรณีเหล่านี้ฉันสามารถจัดการกับอาการของการทดสอบที่ยากด้วยบาล์มอาถรรพณ์ที่ผ่อนคลายหรือฉันสามารถลองหาสาเหตุได้ ฉันรู้สึกเหมือนฉันใช้เวลามากเกินไปในช่วงสองสามปีที่ผ่านมาในอดีตและตอนนี้ฉันต้องการโค้ดที่ดีกว่า

เพื่อตอบคำถามเดิม: ฉันไม่ต้องการ แต่ฉันยังคงใช้letอยู่ ฉันส่วนใหญ่ใช้มันเพื่อให้สอดคล้องกับสไตล์ของทีมอื่น ๆ (ดูเหมือนว่าโปรแกรมเมอร์ของ Rails ส่วนใหญ่ในโลกนี้จะลึกเข้าไปในเวทย์มนตร์ rspec ของพวกเขาดังนั้นมันจึงบ่อยมาก) บางครั้งฉันใช้มันเมื่อฉันเพิ่มการทดสอบในโค้ดบางอย่างที่ฉันไม่สามารถควบคุมได้หรือไม่มีเวลาที่จะ refactor ให้เป็นนามธรรมที่ดีกว่า: นั่นคือเมื่อตัวเลือกเดียวคือยาแก้ปวด


0

ฉันใช้letเพื่อทดสอบการตอบสนอง HTTP 404 ของฉันในรายละเอียด API ของฉันโดยใช้บริบท

let!เพื่อสร้างทรัพยากรที่ผมใช้ letแต่การที่จะเก็บระบุทรัพยากรที่ผมใช้ ดูว่ามันมีลักษณะ:

let!(:country)   { create(:country) }
let(:country_id) { country.id }
before           { get "api/countries/#{country_id}" }

it 'responds with HTTP 200' { should respond_with(200) }

context 'when the country does not exist' do
  let(:country_id) { -1 }
  it 'responds with HTTP 404' { should respond_with(404) }
end

ที่ทำให้รายละเอียดสะอาดและอ่านง่าย

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