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

RSpec for Practical Rubyist

RSpec for Practical Rubyist

@ RubyConfTaiwan 2015

Avatar for Juanito Fatas

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. class Example def self.eq(expected) EqMatcher.new(expected) end end EqMatcher is actually

    BuiltIn::Eq http://git.io/vZffi
  13. class ExpectationTarget def to(matcher) Postive.handle_matcher(actual, matcher) end end Postive is

    actually PositiveExpectationHandler http://git.io/vZfJK
  14. class EqMatcher def matches(actual) match(expected, actual) end private def match(expected,

    actual) actual == expected end end http://git.io/vZfUp
  15. 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*
  16. 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
  17. 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
  18. describe Baz let(:foo) { "bar" } it "…" do ...

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

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

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

    Finished in 42.03924 seconds it "…" do ... end
  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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. setup exercise verify teardown everything we need the real work

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

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

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

  32. ActiveSupport::TestCase ActionController::TestCase ActionMailer::TestCase ActionView::TestCase ActiveJob::TestCase ActionDispatch::IntegrationTest Read these API docs

    on http://api.rubyonrails.org/
  33. 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:"…">
  34. 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
  35. Test like how user use it Use real database records

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

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

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

    attach_file https://github.com/jnicklas/capybara#the-dsl
  39. &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
  40. &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
  41. &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
  42. require "rails_helper" RSpec.describe ShopsController do describe "#index" do ... end

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

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

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

    put head delete ActionController::TestCase
  46. 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
  47. 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
  48. describe "#index" do def do_request get :index end before {

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

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

    context "success" do ... end context "failure" do ... end end DSFBUF
  51. 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
  52. 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
  53. 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
  54. 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
  55. 3BJMT post :show, { id: 1 }, { user_id: 1

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

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

    "flash message" } params session flash
  58. 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
  59. 5JQ Wrap things in Objects, only test message has been

    invoked in controller skinny controller
  60. 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
  61. 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
  62. describe User do context "associations" do; end context "validations" do;

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

    it { expect(subject).to have_many(:repos) } end end &YBN QMF Implicit subject
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. # 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
  70. class UserMailer < ActionMailer::Base def welcome_user(user) @user = user mail(

    to: @user.email, subject: "Welcome to JollyGoodCode!" ) end end &YBN QMF
  71. 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
  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
  74. 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
  75. 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
  76. 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
  77. 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
  78. Routing, Request, View Specs Page Object (Feature Spec) Front-end Testing

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

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

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

    Use Exception monitoring service 4. Find another job
  82. 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 “