Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails Testing Workshop

Rails Testing Workshop

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

Yu-Cheng Chuang

September 26, 2014
Tweet

More Decks by Yu-Cheng Chuang

Other Decks in Programming

Transcript

  1. %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.
  2. "HFOEB ˖ 8IZXFOFFEUFTUJOH ˖ 4FUUJOHVQ34QFD ˖ #BTJD5FTUJOH4ZOUBY ˖ 3FRVFTU5FTUJOH $POUSPMMFS

    
 ˖ 'BDUPSZ ˖ "DUJPO.BJMFS5FTUJOH ˖ 4IBSFE$POUFYU&YBNQMFT ˖ &OEJOH3FGFSFODFT
  3. 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
  4. spec ├── controllers ├── fixtures ├── helpers ├── mailers ├──

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

    dir $ rspec spec/models/ ! # single file $ rspec spec/models/post.rb:30 Can specify line number
  6. 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
  7. 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
  8. EFTDSJCF ˖ "IVNBOSFBEBCMFEFTDSJQUJPOPGBTFUPGUFTUDBTFT ˖ describe "Post" describe "auto reloading" ˖

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

    ˖ -B[ZFWBMVBUFEXJMMOPUCFSVOVOUJMSTUFYFDVUJPO ˖ UPGPSDFFWBMVBUFVQPOEFDMBSBUJPO VTFlet! MFUCBOH
  10. FYQFDU ˖ 5IFNBUDIFSˋXFDIFDLCFIBWJPSTSFTVMUTXJUIJU ˖ expect(post.slug).to eq "blahblah" ˖ 5IFSFBSFNBOZNBUDIFSTJO34QFD DIFDL


    IUUQTSFMJTIBQQDPNSTQFDSTQFDFYQFDUBUJPOT ˖ :PVDBODSFBUFZPVSPXONBUDIFSTUPP OPUDPWFSJOHUPEBZ
  11. TQFDNPEFMTQPTU@TQFDSC ! describe "default state" do # add let and

    subject here ! it "is draft" do # add expect here end end 10 minutes exercise
  12. 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
  13. 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
  14. TUVCCJOH ˖ "TTVNFBOPCKFDUTNFUIPEBMXBZTSFUVSOTBXBOUFEWBMVF • allow(post).to receive(:state) { "draft" } ˖

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

    tests in spec/models/post_spec.rb 20 minutes exercise
  16. 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)
  17. $ git checkout -f exercise-03 &YFSDJTF5JNF Finish the cases that

    does not allow comments 10 minutes exercise spec/requests/comments_spec.rb
  18. 8IBU'BDUPSZ ˖ *LOPX ZPVWFBMSFBEZCFFOUJSFEEPJOHUIJT • let(:post) { Post.create(:title => "blah

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

    FH:PVXBOUUPHFOFSBUFTFRVFOUJBMWBMVFT CPU CPU  ˖ "MTPDBOIPPLNFUIPETEVSJOHHFOFSBUJPO
  20. *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
  21. 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
  22. let(:post) { Post.create(:title => "univ. answer is 42") } let(:post)

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

    spec/requests/comments_spec.rb 15 minutes exercise
  24. )PXUP5FTU"DUJPO.BJMFS ˖ 8FDBOUTFOEUIFFNBJMUPUIFSFBMXPSMEEVSJOHUFTUJOH ˖ *O3BJMT CZEFGBVMU UFTUFOWEPFTOPUTFOEFNBJM ZBZ  ˖

    5IFSFJTBTQFDJBMNBJMCPYUPDPMMFDUPVUHPJOHNBJMT • ActionMailer::Base.deliveries ˖ 8FDPMMFDUUIFNBJMTGSPNUIFSFBOENBUDIBHBJOTUJU
  25. 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)
  26. 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)
  27. 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 }
  28. 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(["[email protected]"]) expect(mail.from).to eq(["[email protected]"]) 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'.
  29. 4IBSFE$POUFYU ˖ shared_context "when post is published" { … }

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

    { Fabricate(:published_post) } end declare a shared context
  31. 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
  32. 4IBSFE&YBNQMFT ˖ shared_examples "not creating a new comment" { …

    } ˖ 6TFJUCMPDLTJOJU:PVDBOIBWFBTNBOZBTZPVXBOU • it_behaves_like "not creating a new comment" ˖ JOEFTDSJCFDPOUFYUCMPDLT ˖ PSJODMVEF@FYBNQMFT
  33. 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
  34. 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