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

Rails Testing Workshop

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Rails Testing Workshop

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

Avatar for Yu-Cheng Chuang

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