Pro Yearly is on sale from $80 to $50! »

Rails Testing Workshop

Rails Testing Workshop

This is the slides used in Rails Pacific 2014 "Testing Workshop".

36b1f565fc83d9b67588123f2171b896?s=128

Yu-Cheng Chuang

September 26, 2014
Tweet

Transcript

  1. 3BJMT5FTUJOH8PSLTIPQ 'PS#FHJOOFST

  2. %PXOMPBE-BUFTU$PEF If you downloaded last night If you never downloaded

    $ git pull -f $ git clone https://github.com/chitsaou/test-workshop.git Then run $ bundle install --local This workshop is for beginners. If you know how to test Rails apps, this workshop is not for you.
  3. :V$IFOH$IVBOH ˖ 3BJMTEFWFMPQFSXJUIZFYQFSJFODFT ˖ 8PSLJOHBU4QPPO3PDLFU*OD ˖ 5XJUUFS!ZPSLYJO

  4. "QPMPHJ[F ˖ *EJEOUQSFQBSFUIJTXPSLTIPQWFSZXFMM ˖ :PVSXPSLTXJMMMPTFBOEXJMMCFPWFSSJEEFOCZNZBOTXFSTEVSJOH UIFXPSLTIPQ ˖ :PVSFHPJOHUPMFBSOWFSZCBTJDUFDIOJRVFTPG34QFDPOMZ ˖ 4PSSZGPSUIBU

    BOEXFMDPNFGPSBOZGFFECBDL
  5. "HFOEB ˖ 8IZXFOFFEUFTUJOH ˖ 4FUUJOHVQ34QFD ˖ #BTJD5FTUJOH4ZOUBY ˖ 3FRVFTU5FTUJOH $POUSPMMFS

    
 ˖ 'BDUPSZ ˖ "DUJPO.BJMFS5FTUJOH ˖ 4IBSFE$POUFYU&YBNQMFT ˖ &OEJOH3FGFSFODFT
  6. 'PS&WFSZPOFT(PPE ˖ .BOVBMMZ2"JTUJSJOH.BDIJOFEPSFQFBUJOHKPCTCFUUFS ˖ &OTVSFUIBUDPEFDIBOHFTEPFTOPUCSFBLPUIFSUIJOHT ˖ *GUFTUGBJMT EPOUNFSHFBOEEPOUSFMFBTF ˖ 4BWFZPVSTFMG

    TBWFZPVSDPMMFBHVFT BOETBWFUIFXPSME
  7. 34QFD

  8. 34QFD ˖ "UFTUJOHGSBNFXPSL ˖ 5IFNPTUVTFEPOFJO3VCZXPSME OPUPOMZGPS3BJMT  ˖ 5POTPGQPXFSGVMQMVHJOTJODMVEJOHCSPXTFSCBTFEUFTUJOH DBQZCBSB

     ˖ &BTZUPVOEFSTUBOE4ZOUBY
  9. 4FUVQ34QFD (I've done this for you, so you don't have

    to do this) # Gemfile group :development, :test do gem 'rspec-rails', '~> 3.1.0' end ! # Run $ bundle install $ rails generate rspec:install
  10. spec ├── controllers ├── fixtures ├── helpers ├── mailers ├──

    models ├── requests ├── routing ├── views ├── spec_helper.rb └── rails_helper.rb ✔ ✔ ✔ ✔
  11. 3VO5FTU$BTFT # all files $ rspec spec/ ! # specify

    dir $ rspec spec/models/ ! # single file $ rspec spec/models/post.rb:30 Can specify line number
  12. #BTJD34QFD4ZOUBY

  13. $ git checkout -f rev-01

  14. BQQNPEFMTQPTUSC class Post < ActiveRecord::Base before_create :generate_slug ! private def

    generate_slug self.slug = self.title.gsub(/[^A-Za-z0-9]+/, '-').downcase end end
  15. TQFDNPEFMTQPTU@TQFDSC require 'rails_helper' ! RSpec.describe Post, :type => :model do

    describe "#slug" do let(:post) { Post.create(:title => "The answer to everything & the university = 42") } ! subject { post.slug } ! it "generates proper slug" do expect(subject).to eq("the-answer-to-everything-the-university-42") end end end
  16. EFTDSJCF ˖ "IVNBOSFBEBCMFEFTDSJQUJPOPGBTFUPGUFTUDBTFT ˖ describe "Post" describe "auto reloading" ˖

    $BOCFBOZTUSJOH ˖ 6TVBMMZBNFUIPEOBNFJTTUBSUFEXJUI# FH#publish
  17. MFU ˖ EFDMBSFTTVQQMFNFOUBMPCKFDUTJOUIJTTDPQF JOdescribeFUD  ˖ let(:post) { Post.create(…) }

    ˖ -B[ZFWBMVBUFEXJMMOPUCFSVOVOUJMSTUFYFDVUJPO ˖ UPGPSDFFWBMVBUFVQPOEFDMBSBUJPO VTFlet! MFUCBOH
  18. TVCKFDU ˖ EFDMBSFTUIFTVCKFDUDIBSBDUFSPGZPVSUFTUDBTFTJOBTDPQF ˖ subject { post.slug } ˖ PGDPVSTFZPVDBOVTFMFUTVCKFDUJTKVTUBTZOUBYTVHBS

    ˖ UIFSFBSFNPSFTZOUBYTVHBSTXJUIJU FHits OPUDPWFSJOHUPEBZ
  19. FYQFDU ˖ 5IFNBUDIFSˋXFDIFDLCFIBWJPSTSFTVMUTXJUIJU ˖ expect(post.slug).to eq "blahblah" ˖ 5IFSFBSFNBOZNBUDIFSTJO34QFD DIFDL


    IUUQTSFMJTIBQQDPNSTQFDSTQFDFYQFDUBUJPOT ˖ :PVDBODSFBUFZPVSPXONBUDIFSTUPP OPUDPWFSJOHUPEBZ
  20. &BTZ IVI $ git checkout -f exercise-01 Let's try it

  21. BQQNPEFMTQPTUSC class Post < ActiveRecord::Base before_create :set_draft ! private def

    set_draft self.state = "draft" end end
  22. TQFDNPEFMTQPTU@TQFDSC ! describe "default state" do # add let and

    subject here ! it "is draft" do # add expect here end end 10 minutes exercise
  23. TQFDNPEFMTQPTU@TQFDSC ! describe "default state" do let(:post) { Post.create(:title =>

    "Hello! World") } ! subject { post.state } ! it "is draft" do expect(subject).to eq "draft" end end $ git checkout -f answer-01
  24. $ git checkout -f rev-02

  25. BQQNPEFMTQPTUSC class Post < ActiveRecord::Base def publish! self.update(:state => "published")

    end end
  26. TQFDNPEFMTQPTU@TQFDSC describe "#publish!" do let(:post) { Post.create(:title => "Don't Care")

    } ! before { post.publish! } ! subject { post.state } ! it "becomes published" do expect(subject).to eq "published" end end
  27. CFGPSF ˖ 3VOTDPEFTOJQQFUTCFGPSFFBDIUFTUDBTFJOUIJTTDPQF ˖ before { post.publish! } ˖ (PPEGPSQSFQBSJOHGPSUIFUBSHFUPCKFDU

    ˖ 5IFSFBSFBMTPafterBOEaround OPUDPWFSJOHUPEBZ
  28. DPOUFYU ˖ *G$POEJUJPOGPS34QFD CVUGPSIVNBOSFBEJOHPOMZ ˖ context "when the post is

    published" do …
  29. TUVCCJOH ˖ "TTVNFBOPCKFDUTNFUIPEBMXBZTSFUVSOTBXBOUFEWBMVF • allow(post).to receive(:state) { "draft" } ˖

    6TFGVMGPSPCKFDUTUBUVTBTTVNQUJPO ˖ FHBTTVNFUIBUpost.stateBMXBZTSFUVSOT"draft" ˖ 6TVBMMZSVOTJOBCFGPSFCMPDL
  30. $ git checkout -f exercise-02 &YFSDJTF5JNF Finish #can_publish? and #to_param

    tests in spec/models/post_spec.rb 20 minutes exercise
  31. $ git checkout -f answer-02 "OTXFSTUP&YFSDJTF

  32. 3FRVFTU5FTUJOH GPS$POUSPMMFST

  33. 8IBUJT3FRVFTU5FTUJOH ˖ )5513FRVFTUUFTUJOH ˖ 1FSGPSNTIJHIMFWFM)551SFRVFTUUFTUJOHBHBJOTUDPOUSPMMFST ˖ $BSFTBCPVU)551SFTQPOTFTJOTUFBEPGWBSJBCMFTJUVTFE ˖ 6TFGVMGPSUFTUJOH"1*T

  34. 8IZ3FRVFTU5FTUJOH ˖ $POUSPMMFSIBOEMFT)551SFRVFTUT ˖ 5FTUJOHBDPOUSPMMFSMJLFBCMBDLCPYBUIJHIFSMFWFMJTCFUUFS ˖ UIBOUFTUJOHBDPOUSPMMFSTEFUBJMFEWBSJBCMFBTTJHONFOUT

  35. $ git checkout -f rev-03

  36. BQQDPOUSPMMFSTDPNNFOUT@DPOUSPMMFSSC def create @comment = @post.comments.build(comment_params) ! if @comment.save redirect_to

    post_url(@post) else flash.alert = "(skip…)" render "posts/show" end end
  37. TQFDDPOUSPMMFSTDPNNFOUT@DPOUSPMMFS@TQFDSC RSpec.describe "Comments", :type => :request do describe "POST /posts/xxx/comments"

    do subject { post "/posts/1/comments", :comment => comment_params } ! let(:comment_params) { {:author => "John Appleseed", :content => "Good article. I like it." } } ! context "a post that is published" do before { the_post.publish! } ! it "creates a new comment" do expect { subject }.to change { the_post.comments.count }.by(1) ennnnd request tests always tagged as request describe is usually written as HTTP request syntax subject is defined as "executing a http request" matching against database changes (will write to DB)
  38. $ git checkout -f exercise-03 &YFSDJTF5JNF Finish the cases that

    does not allow comments 10 minutes exercise spec/requests/comments_spec.rb
  39. 'BDUPSZ

  40. 8IBU'BDUPSZ ˖ *LOPX ZPVWFBMSFBEZCFFOUJSFEEPJOHUIJT • let(:post) { Post.create(:title => "blah

    blah") } ˖ *KVTUEPOUDBSFBCPVUUIFEFUBJMFEBUUSJCVUFT ˖ *UTUJNFGPS'BDUPSZ
  41. 'BDUPSZ ˖ 0CKFDUTHFOFSBUPS XJUIEFGBVMU SBOEPN EFUBJMFEBUUSJCVUFT ˖ FH:PVEPOUDBSFBVTFSTOBNF BQPTUTUJUMF ˖

    FH:PVXBOUUPHFOFSBUFTFRVFOUJBMWBMVFT CPU CPU  ˖ "MTPDBOIPPLNFUIPETEVSJOHHFOFSBUJPO
  42. 'BCSJDBUJPO HFN ˖ 0OFPGGBDUPSZQMVHJOTGPS3VCZ BOPUIFSPOFJT'BDUPSZ(JSM  ˖ &BTZUPVTFCVUGFXFSGFBUVSFTUIBO'BDUPSZ(JSM

  43. 'BLFS HFN ˖ 3BOEPNEBUBHFOFSBUPS ˖ (FOFSBUFTTFOUFODFT XPSET IVNBOOBNFT FNBJMT UFMFQIPOF

    OVNCFST FUD ˖ /PNPSFUIJOLJOHBCPVUFYBNQMFXPSET
  44. $ git checkout -f rev-04

  45. *OTUBMMBUJPO group :development, :test do gem 'faker' end ! group

    :test do gem 'fabrication' end (I've done this for you, so you don't have to do this) fabrication is only recommended for test env
  46. TQFDGBCSJDBUPSTQPTU@GBCSJDBUPSSC Fabricator(:post) do title { Faker::Lorem.sentence } body { Faker::Lorem.paragraph(3)

    } end ! Fabricator(:published_post, :from => :post) do after_create { |post| post.publish! } end
  47. let(:post) { Post.create(:title => "Don't Care") } let(:post) { Fabricate(:post)

    }
  48. let(:post) { Post.create(:title => "univ. answer is 42") } let(:post)

    { Fabricate(:post, :title => "univ. answer is 42") }
  49. $ git checkout -f exercise-04 &YFSDJTF5JNF Simplify 4 let's in

    spec/requests/comments_spec.rb 15 minutes exercise
  50. $ git checkout -f answer-02 "OTXFSTUP&YFSDJTF

  51. "DUJPO.BJMFS5FTUJOH

  52. )PXUP5FTU"DUJPO.BJMFS ˖ 8FDBOUTFOEUIFFNBJMUPUIFSFBMXPSMEEVSJOHUFTUJOH ˖ *O3BJMT CZEFGBVMU UFTUFOWEPFTOPUTFOEFNBJM ZBZ  ˖

    5IFSFJTBTQFDJBMNBJMCPYUPDPMMFDUPVUHPJOHNBJMT • ActionMailer::Base.deliveries ˖ 8FDPMMFDUUIFNBJMTGSPNUIFSFBOENBUDIBHBJOTUJU
  53. $ git checkout -f 0753cce

  54. 4FUVQ # spec/macros/mailer_macros.rb module MailerMacros def last_email ActionMailer::Base.deliveries.last end !

    def reset_email ActionMailer::Base.deliveries = [] end end (I've done this for you, so you don't have to do this)
  55. 4FUVQ (I've done this for you, so you don't have

    to do this) ! # spec/spec_helper.rb require "macros/mailer_macros" … config.include(MailerMacros)
  56. TQFDNBJMFST BENJO@OPUJDBJUPO@NBJMFS@TQFDSC RSpec.describe AdminNotificaitonMailer, :type => :mailer do describe "new_comment"

    do let(:comment) { Fabricate(:comment) do post { Fabricate(:post, :title => "Awesome Article") } end } ! before { AdminNotificaitonMailer.new_comment(comment).deliver }
  57. TQFDNBJMFST BENJO@OPUJDBJUPO@NBJMFS@TQFDSC let(:mail) { last_email } ! it "renders the

    headers" do expect(mail.subject).to eq("[Blog] Awesome Article got a new comment.") expect(mail.to).to eq(["admin@example.org"]) expect(mail.from).to eq(["no-reply@example.com"]) end ! it "renders the body" do expect(mail.body.encoded).to match("Awesome Article got a new comment.") end end We can use 'subject' but it will be confused with 'subject of mail'.
  58. (sorry, no exercise)

  59. #VUXIBUJG*XBOUUPTFFJU ˖ 6TFMFUUFS@PQFOFSIUUQTHJUIVCDPNSZBOCMFUUFS@PQFOFS ˖ 8IFOZPVTFOEBOFNBJMJOEFWFMPQNFOUFOW JUPQFOTUIFFNBJM JOZPVSCSPXTFS ˖ #FXBSFPGCBUDIFNBJM:PVSCSPXTFSXJOEPXXJMMCFCMPBUFE

  60. 4IBSFE$POUFYU&YBNQMFT

  61. 4IBSFEDPOUFYUFYBNQMFT ˖ 8FDBODPMMFDUTBNFDPOUFYUTFYBNQMFTUPHFUIFS ˖ "OESFVTFUIFNJOEFTDSJCFDPOUFYU

  62. $ git checkout -f rev-05

  63. 4IBSFE$POUFYU ˖ shared_context "when post is published" { … }

    ˖ 6TFMFUCFGPSFTVCKFDUJOJU KVTUMJLFBOPSNBMDPOUFYU • include_context "when post is published" ˖ JOEFTDSJCFDPOUFYUCMPDLT
  64. TQFDNPEFMTQPTU@TQFDSC shared_context "a post whose state is published" do let(:post)

    { Fabricate(:published_post) } end declare a shared context
  65. TQFDNPEFMTQPTU@TQFDSC context "a post whose state is published" do include_context

    "a post whose state is published" context "a post whose state is published" do let(:post) { Fabricate(:published_post) } include it in another context
  66. 4IBSFE&YBNQMFT ˖ shared_examples "not creating a new comment" { …

    } ˖ 6TFJUCMPDLTJOJU:PVDBOIBWFBTNBOZBTZPVXBOU • it_behaves_like "not creating a new comment" ˖ JOEFTDSJCFDPOUFYUCMPDLT ˖ PSJODMVEF@FYBNQMFT
  67. TQFDSFRVFTUTDPNNFOUT@TQFDSC shared_examples "not creating a new comment" do it "does

    not create a new comment" do expect { subject rescue nil # ignore any exception }.not_to change { the_post.comments.count } end end declare a shared example
  68. TQFDSFRVFTUTDPNNFOUT@TQFDSC it_behaves_like "not creating a new comment" it "does not

    create a new comment" do expect { subject rescue nil # ignore any exception }.not_to change { the_post.comments.count } end include it in a context or description
  69. (sorry, no exercise)

  70. 8"3/*/( ˖ *UTLJOEBSFGBDUPSJOHCVUEPOUEPJUUPPNVDI ˖ 5FTUTTIPVMECFTUVQJEBOEFBTZUPSFBE FWFOXEVQMJDBUJPOT ˖ %POUNJYTIBSFEFYBNQMFTBOETIBSFEDPOUFYUTUPHFUIFSˋXJMM CFDPNFIBSEUPSFBEFWFOUVBMMZ ˖

    #BETUSVDUVSFETIBSFEHSPVQTNBLFUFTUTIBSEUPSFBE
  71. &OEJOH

  72. 3FGFSFODFT ˖ 34QFD%PDIUUQSTQFDJOGP ˖ &WFSZEBZ3BJMT5FTUJOHXJUI34QFDIUUQTMFBOQVCDPN FWFSZEBZSBJMTSTQFD