Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

RSpec for Practical Rubyist

RSpec for Practical Rubyist

@ RubyConfTaiwan 2015

Juanito Fatas

September 11, 2015
Tweet

More Decks by Juanito Fatas

Other Decks in How-to & DIY

Transcript

  1. RSPEC FOR THE PRACTICAL RUBYIST EXPLAINING SIMPLY AND QUICKLY ALL

    THE ELEMENTS OF CONTROLLER, MODEL, VIEW, HELPER, INTEGRATION, FEATURE WITHout ANSWERS TO PROBLEMS
  2. rubytaiwan/taipei Things to know about Taipei put together by Ruby

    Taiwan Community Inspired from RubySG/Singapore
  3. %

  4. RSPEC FOR THE PRACTICAL RUBYIST EXPLAINING SIMPLY AND QUICKLY ALL

    THE ELEMENTS OF CONTROLLER, MODEL, VIEW, HELPER, INTEGRATION, FEATURE WITHout ANSWERS TO PROBLEMS
  5. correctness quality buying time for the future finding regressions save

    repeated labor gives you confidence easy refactoring perfect bug report live documentation drive your implementation automation is win ✨
  6. A professional developer should be able to work in either

    one of these because they essentially do the same thing: test your code. — Dr.Tenderlove “ http://tenderlovemaking.com/2015/01/23/my-experience-with-minitest-and-rspec.html
  7. class Something < Minitest::Test def test_works assert_equal 11, 10 end

    def test_really_works assert_equal 11, 11 end end (Unit)Test
  8. describe "something" do it "works" do expect(11).to equal(10) end it

    "really works" do expect(11).to equal(11) end end Spec(ification)
  9. Test cases are describe with description string / actual object

    and a block of tests Tests are it with description string and the body of test (block)
  10. Computer science is a terrible name for this business. First

    of all, it's not a science. It might be engineering or it might be art, but we'll actually see that computer so-called science actually has a lot in common with magic.— Hal Abelson “ https://youtu.be/2Op3QLzMgSY?t=27s
  11. describe it expect to a series of tests is a

    test an actual object match something
  12. eq eql equal be be_* match be_within start_with end_with be_instance_of

    be_kind_of respond_to raise_error throw_symbol include match_array contain_exactly cover change satisfy output yield_* https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers "1*
  13. expect { code }.to raise_error(error, message) expect { post :create

    } .to raise_error( ActiveRecord::RecordInvalid, "Validation failed: name can't be blank" ) raise_error matcher
  14. expect { code }.to change { something }.by(value) def do_request

    post :create, user: params end expect { do_request }.to change { User.count }.by(1) change matcher
  15. describe Baz let(:foo) { "bar" } it "…" do ...

    end end foo will be available in it blocks under the same scope!
  16. let(:foo) { sleep 42 } it "…" do ... end

    Finished in 0.04424 seconds it "…" do ... end
  17. let(:foo) { sleep 42 } it "…" do foo end

    Finished in 42.03924 seconds it "…" do ... end
  18. let!(:foo) { sleep 42 } it "…" do ... end

    Finished in 42.03924 seconds it "…" do ... end
  19. context "stub and mock" do describe "stub" do it "won't

    verify" do allow(User).to receive(:new) # User.new end end describe "mock" do it "will verify" do expect(User).to receive(:new) # User.new end end end
  20. context "stub and mock" do describe "stub" do it "won't

    verify" do allow(User).to receive(:new) # User.new end end describe "mock" do it "will verify" do expect(User).to receive(:new) # User.new end end end Stub
  21. context "stub and mock" do describe "stub" do it "won't

    verify" do allow(User).to receive(:new) # User.new end end describe "mock" do it "will verify" do expect(User).to receive(:new) # User.new end end end Mock
  22. context "stub and mock" do describe "stub" do it "won't

    verify" do allow(User).to receive(:new) # User.new end end describe "mock" do it "will verify" do expect(User).to receive(:new) # User.new end end end Failure/Error: expect(User).to receive(:new) Mock
  23. context "stub and mock" do describe "stub" do it "stub

    and verify" do allow(User).to receive(:new) User.new expect(User).to have_received(:new) end end end
  24. context "stub and mock" do describe "stub" do it "won't

    verify" do allow(User).to receive(:new) User.new expect(User).to have_received(:new) end end end Strategy Spaces
  25. setup exercise verify teardown everything we need the real work

    our expectations back to original state
  26. before { user = User.new(password: "passw0rd") } it "should encrypt

    password" do user.save expect(user.encrypted_password).not_to eq nil end Put together
  27. it "should encrypt password" do user = User.new(password: "passw0rd") user.save

    expect(user.encrypted_password).not_to eq nil end Strategy Spaces
  28. Spy

  29. Fixture # spec/fixtures/users.yml juanitofatas: name: JuanitoFatas email: [email protected] # spec/rails_helper.rb

    RSpec.configure do |config| config.fixture_path = "#{::Rails.root}/spec/fixtures" config.use_transactional_fixtures = true end # under describe or context users :juanitofatas # => #<User:0x1234 name: "JuanitoFatas" email:"…">
  30. RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods end $PO H create(:user) instead

    of FactoryGirl.create(:user) https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
  31. Test like how user use it Use real database records

    Don’t mock or stub objects External Service can stub 5JQ
  32. &YBN QMF feature "Auth" do given { ... } background

    { ... } scenario "show name" do ... end end
  33. RSpec.feature "…" do end Capybara API will be available in

    here http://www.rubydoc.info/github/jnicklas/capybara/master#The_DSL
  34. "1* page visit current_path click_on find fill_in choose check select

    attach_file https://github.com/jnicklas/capybara#the-dsl
  35. &YBN QMF feature "Sign In" do scenario "works" do visit

    root_path click_on "Sign In" fill_in "login", with: "JuanitoFatas" fill_in "password", with: "1passw0rd" click_on "Sign In" end end
  36. &YBN QMF feature "Auth" do scenario "show name" do create(:user,

    name: "Juan") sign_in_as(user) expect(page).to have_content("Juan") end end
  37. &YBN QMF feature "Vote" do scenario "upvote" do create(:user, name:

    "Juan") sign_in_as(user) expect(page).to have_content("Votes 0") find("upvote").click expect(page).to have_content("Votes 1") end end
  38. require "rails_helper" RSpec.describe ShopsController do describe "#index" do ... end

    describe "#new" do ... end describe "#create" do ... end … $POWFOUJPO
  39. describe "#show" do ... end describe "#edit" do ... end

    describe "#update" do ... end describe "#destroy" do ... end end $POWFOUJPO
  40. 5JQ Ordering should be the same as your actions in

    controller index new create show edit update destroy
  41. params request response session flash cookie "1* get post patch

    put head delete ActionController::TestCase
  42. RSpec.describe HomeController do describe "#index" do def do_request get :index

    end it "works" do do_request expect(response).to be_success end end end JOEFY
  43. describe HomeController do describe "#index" do def do_request get :index

    end it "works" do do_request expect(response).to be_success end end end &YBN QMF same scope
  44. describe "#index" do def do_request get :index end before {

    do_request } it { expect(response).to be_success } end JOEFY
  45. describe "#new" do def do_request get :new end before {

    do_request } it { expect(response).to be_success } end OFX
  46. describe "#create" do def do_request post :create, thing: params end

    context "success" do ... end context "failure" do ... end end DSFBUF
  47. describe "#show" do let(:thing) { create(:thing) } def do_request get

    :show, id: thing end before { do_request } it { expect(response).to be_success } end TIPX
  48. FEJU describe "#edit" do let(:thing) { create(:thing) } def do_request

    get :edit, id: thing.id end before { do_request } it { expect(response).to be_success } end
  49. describe "#update" do let(:thing) { create(:thing) } def do_request patch

    :update, id: thing.id, thing: params end before { do_request } context "success" do ... end context "failure" do ... end end VQEBUF
  50. describe "#destroy" do let!(:thing) { create(:thing) } def do_request delete

    :destroy, id: thing.id end it "redirects" do expect { do_request }.to change(Thing, :count).by(-1) expect(response).to redirect_to things_path end end EFTUSPZ
  51. 3BJMT post :show, { id: 1 }, { user_id: 1

    }, { notice: "flash message" }
  52. 3BJMT post :show, { id: 1 }, {}, { notice:

    "flash message" } params session flash
  53. 3BJMT post :show, { shop_id: 1 }, {}, { notice:

    "flash message" } params session flash
  54. 3BJMT post :show, params: { shop_id: 1 }, session: {

    user_id: 1 }, flash: { notice: "flash message" } Rails 5 Switch to Keyword Arguments https://github.com/rails/rails/pull/18323
  55. 5JQ Wrap things in Objects, only test message has been

    invoked in controller skinny controller
  56. describe "#create" do context "success" do it "sent welcome mail"

    do allow(UserMailer).to receive(:welcome) do_request expect(UserMailer).to have_received(:welcome) end end end &YBN QMF
  57. describe "#create" do context "success" do it "sent welcome mail"

    do allow(Payment).to receive(:charge!) do_request expect(Payment).to have_received(:charge!) end end end
  58. describe User do context "associations" do; end context "validations" do;

    end describe ".class_method" do; end describe "#instance_method" do; end end $POWFOUJPO
  59. describe User do subject { User.new } context "associations" do

    it { expect(subject).to have_many(:repos) } end end &YBN QMF Implicit subject
  60. Post.trending scope :trending, -> { order("upvotes DESC") } describe Post

    do describe ".trending" do popular = create(:post, upvotes: 42) just_created = create(:post, upvotes: 1) trending_posts = Post.trending expect(trending_posts).to \ eq [popular, just_created] end end
  61. require "rails_helper" describe EventsHelper do describe "#linkify_event" do it "works"

    do expect(linkify_event).to eq "RubyKaigi 11st-13rd Dec, 2015" end end end &YBN QMF
  62. require "rails_helper" describe EventsHelper do describe "#linkify_event" do it "works"

    do expect(linkify_event).to eq "RubyKaigi 11st-13rd Dec, 2015" end end end EventsHelper::LinkifyEvent
  63. require "rails_helper" describe EventsHelper do describe "#linkify_event" do it "works"

    do self expect(linkify_event).to eq "RubyKaigi 11st-13rd Dec, 2015" end end end RSpec::ExampleGroups:: EventsHelper::LinkifyEvent
  64. require "rails_helper" describe EventsHelper do describe "#linkify_event" do it "works"

    do expect(linkify_event).to eq "RubyKaigi 11st-13rd Dec, 2015" end end end
  65. # Either in spec/rails_helper.rb or # spec/email_helper.rb require "email_spec" RSpec.configure

    do |config| config.include EmailSpec::Helpers config.include EmailSpec::Matchers end $PO H
  66. class UserMailer < ActionMailer::Base def welcome_user(user) @user = user mail(

    to: @user.email, subject: "Welcome to JollyGoodCode!" ) end end &YBN QMF
  67. RSpec.describe UserMailer do describe "#welcome_user" do let(:user) { build_stubbed(:user) }

    let(:email) { email_for(user) } def email_for(user) UserMailer.welcome_user(user) end it { expect(email).to have_subject "Welcome to JollyGoodCode!" } it { expect(email).to deliver_to user.email } end end &YBN QMF
  68. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    # stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF
  69. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF
  70. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF
  71. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF
  72. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF
  73. it "webmock example" do url = "https://api.github.com/users/juanitofatas" fixture = "spec/fixtures/github/juanitofatas.json"

    stub_request(:get, url). to_return(body: File.read(fixture)) HTTP.get(url) expect(WebMock).to have_requested(:get, url) expect(a_request(:get, url)).to have_been_made end &YBN QMF https://github.com/sferik/t
  74. Routing, Request, View Specs Page Object (Feature Spec) Front-end Testing

    Infrastructure Testing A/B Testing Smoke Testing (example)
  75. Routing Specs Request Specs View Specs Page Object (Feature Spec)

    Front-end Testing Infrastructure Testing A/B Testing Smoke Testing (example) NOT COVERED
  76. 1. Write Feature Spec 2. New things with specs 3.

    Use Exception monitoring service
  77. 1. Write Feature Spec 2. New things with specs 3.

    Use Exception monitoring service 4. Find another job
  78. I get paid for code that works, so my philosophy

    is to test as little as possible to reach a given level of confidence.— Kent Back “