RSpec on Rails Tutorial https://ihower.tw 2016/8 Agenda Rails - - PowerPoint PPT Presentation

rspec on rails tutorial
SMART_READER_LITE
LIVE PREVIEW

RSpec on Rails Tutorial https://ihower.tw 2016/8 Agenda Rails - - PowerPoint PPT Presentation

RSpec on Rails Tutorial https://ihower.tw 2016/8 Agenda Rails RSpec Model Spec, Routing Spec, Controller Spec, View Spec, Helper Spec Request Spec Feature Spec


slide-1
SLIDE 1

RSpec on Rails Tutorial

https://ihower.tw 2016/8

slide-2
SLIDE 2

Agenda

  • Rails RSpec
  • Model Spec, Routing Spec, Controller Spec,

View Spec, Helper Spec

  • Request Spec Feature Spec
  • CI (Continuous Integration)
  • Web
slide-3
SLIDE 3

Install rspec-rails

  • gem “rspec-rails”
  • bundle
  • rails g rspec:install
  • git rm -r test
slide-4
SLIDE 4

rake -T spec

  • rake spec
  • bundle exec rspec rspec/xxx/xxx
slide-5
SLIDE 5

Generators

  • rails g model A
  • rails g controller B
  • rails g scaffold C
slide-6
SLIDE 6

spec/rails_helper.rb spec/spec_helper.rb

config.fail_fast = true config.profile_examples = 3 config.order = :random

slide-7
SLIDE 7

More Matchers

  • expect(target).to eq(XXX)
  • expect{ Post.find(9999) }.to

raise_error(ActiveRecord::RecordNotFound)

  • expect(target).to be_xxx # target.xxx?
  • expect(target).to be_a_xxx
  • expect(target).to be_an_xxx
  • expect(collection).to be_empty
  • expect([1,2,3]).to be_include(3)
  • expect({ foo: "foo" }).to have_key(:foo)
  • expect(3).to be_a_kind_of(Fixnum)
  • Custom matcher
slide-8
SLIDE 8

rspec-rails

  • model
  • controller (stub/mock)
  • view
  • helper
  • routing
  • controller ( stub/mock model )
  • (controllers)
  • request
  • feature ( capybara)
slide-9
SLIDE 9

https://robots.thoughtbot.com/rails-test-types-and-the-testing-pyramid

slide-10
SLIDE 10

Model spec syntax

let(:valid_attributes){ { :name => "Train#123"} } expect(Event.new).to_not be_valid expect(Event.new(valid_attributes)).to_not be_valid

slide-11
SLIDE 11

Exercise 0

  • Rails (ticket_office)
  • rspec-rails gem
  • scaffold
slide-12
SLIDE 12

Exercise 1: Train Model Spec

  • Train model
  • valid
slide-13
SLIDE 13

Kata

  • Ticket Office
  • GET /trains/{train_id}
  • POST /trains/{train_id}/reservations
slide-14
SLIDE 14

Routing spec syntax

expect(:get => "/events").to route_to("events#index") expect(:get => "/widgets/1/edit").not_to be_routable

slide-15
SLIDE 15

But…

  • ( Rails

)

  • resources routing specmodel

validations associations

  • custom route
slide-16
SLIDE 16

Controller spec syntax

get :show post :create, :params => { :user => { :name => "a" } } patch :update delete :destroy # more arguments request.cookies[:foo] = "foo"
 request.session[:bar] = “bar" post :create, :params => { :name => "a" }, :session => { :zoo => "zoo" }, :flash => { :notice => "c"}, :format => :html

: params Rails 5.0

slide-17
SLIDE 17

Matcher syntax

expect(response).to render_template(:new) expect(response).to redirect_to(events_url) expect(response).to have_http_status(200) expect(assigns(:event)).to be_a_new(Event)

slide-18
SLIDE 18

Isolation Mode

  • controller spec render view

RSpec

  • render_views
slide-19
SLIDE 19

Exercise 2: Train Controller show spec (stub version)

  • trains/show
  • Train#find stub

DB

slide-20
SLIDE 20

View

isolated from controller too

assign(:widget, double(“Widget”, :name => "slicer")) render expect(rendered).to match /slicer/

slide-21
SLIDE 21

Helper spec syntax

expect(helper.your_method).to eq("Q_Q")

slide-22
SLIDE 22

Exercise 3: Train show view

  • train show json view
  • rails4 jbuilder
  • Train

stub

slide-23
SLIDE 23

Exercise 4:

  • Train, Seat, SeatReservation,

Reservation models

  • Train#available_seats
  • controller view stub

(orPartial Stub)

slide-24
SLIDE 24

What have we learned?

  • stub&mock

stub&mocks

  • ActiveRecord
slide-25
SLIDE 25

Exercise 5:

  • ReservationsController
  • Train#reserve mock
  • Train#reserve spec
  • ReservationsController mock
slide-26
SLIDE 26

Exercise 5`:

  • Train#reserve spec
  • ReservationsController

( mock)

slide-27
SLIDE 27

Exercise 6:

  • GET /trains/{id}
  • POST /trains/{id}/reservations
  • POST /trains/{id}/reservations
slide-28
SLIDE 28

Factory v.s. Fixtures

  • rails fixtures

YAML DB

  • model

validation

  • factory ActiveRecord
  • factory_girl gem fabrication gem
  • ActiveReocrd
  • factory_girl trait

unit test

  • model object DB build

create build_stubbed

slide-29
SLIDE 29

factory_girl

FactoryGirl.define do factory :user do firstname "John" lastname "Doe" sequence(:email) { |n| "test#{n}@example.com"} association :profile, :factory => :profile end factory :profile do bio "ooxx" end end

slide-30
SLIDE 30

factory_girl

before do @user = build(:user) # DB @event = create(:event) # DB end it "should post user data" post :create, :params => { :user => attributes_for(:user) } # ... end

slide-31
SLIDE 31
  • https://github.com/thoughtbot/factory_girl/

blob/master/GETTING_STARTED.md

  • https://thoughtbot.com/upcase/videos/

factory-girl

slide-32
SLIDE 32

Tip: support

Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # support/factory_helpers.rb module FactoryHelpers # ... end Rspec.configure do |config| config.include FactoryHelpers end

slide-33
SLIDE 33

Exercise 7: Extract to factory method

  • Train Extract support/

factory.rb

slide-34
SLIDE 34

Tip: stub

before(:each) { allow(controller).to receive(:current_user) { ... } }

slide-35
SLIDE 35

Tip: focus

  • :focus => true describe

it

  • rspec --tag focus

config.filter_run :focus => true config.run_all_when_everything_filtered = true

slide-36
SLIDE 36

Request

  • full-stack
  • stub
  • Web APIs JSON, XML
  • Request

controllers

  • sessions ()
  • Matchers controller spec
slide-37
SLIDE 37

Request spec syntax

describe "GET /events" do it "works! (now write some real specs)" do get “/events” expect(response).to have_http_status(200) end end

slide-38
SLIDE 38

Example: controller

it "creates a Widget and redirects to the Widget's page" do get "/widgets/new" expect(response).to render_template(:new) post "/widgets", :widget => {:name => "My Widget"} expect(response).to redirect_to(assigns(:widget)) follow_redirect! expect(response).to render_template(:show) expect(response.body).to include("Widget was successfully created.") end

slide-39
SLIDE 39

Exercise 8:

  • 1. 2. 3.
slide-40
SLIDE 40

Feature spec

  • capybara gem request spec
  • http://rubydoc.info/github/jnicklas/capybara/

master

  • Capybara HTML
slide-41
SLIDE 41

Capybara example

  • feature "signing up" do

background do User.create(:email => 'user@example.com', :password => 'caplin') end scenario "signing in with correct credentials" do visit "/" # or root_path click_link 'Log In' within("#session") do fill_in 'Login', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' choose('some_select_option_yes') check('some_checkbox') end click_button 'Sign in' expect(User.count).to eq(1) # you can test model expect(page).to have_content 'Login successfuuly' # and/or test page end end

find css selector xpath

slide-42
SLIDE 42

Debugging

  • save_and_open_page
  • capybara-screenshot gem
slide-43
SLIDE 43

JavaScript Driver

  • Capybara javascript
  • javascript_driver

Ruby README

  • https://github.com/teampoltergeist/poltergeist

PhantomJS

  • https://github.com/thoughtbot/capybara-webkit

QtWebKit

  • https://rubygems.org/gems/selenium-webdriver

Firefox

  • test js: true
slide-44
SLIDE 44

JavaScript Driver

  • Browser tools Rails Ruby

thread

  • DB transaction database cleaner
  • https://github.com/DatabaseCleaner/database_cleaner
  • https://github.com/amatsuda/database_rewinder
  • javascript (

Ajax)

  • Capybara 5
  • Capybara.default_wait_time
  • using_wait_time(2) { …. }

https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara

slide-45
SLIDE 45
  • Extract behavior to helper methods
  • Page Object

https://robots.thoughtbot.com/acceptance-tests-at-a-single-level-of- abstraction

slide-46
SLIDE 46

Page Object

http://www.infoq.com/cn/articles/martin-fowler-basic-rule-of-thumbon-for-Web-testing

slide-47
SLIDE 47

Page Object example

https://teamgaslight.com/blog/6-ways-to-remove-pain-from-feature-testing-in-ruby-on-rails https://thoughtbot.com/upcase/videos/page-objects https://robots.thoughtbot.com/better-acceptance-tests-with-page-objects https://medium.com/neo-innovation-ideas/clean-up-after-your-capybara-1a08b47a499b#.oyl7zi44d https://www.sitepoint.com/testing-page-objects-siteprism/

slide-48
SLIDE 48

(1)

  • Debugging ?
  • puts
  • https://tenderlovemaking.com/2016/02/05/i-am-

a-puts-debuggerer.html

  • byebug
  • --only-failures option
  • https://relishapp.com/rspec/rspec-core/docs/

command-line/only-failures

slide-49
SLIDE 49

(2)

  • Time.now ?
  • http://api.rubyonrails.org/classes/

ActiveSupport/Testing/TimeHelpers.html travel_to

  • config.include

ActiveSupport::Testing::TimeHelpers

  • Timecop gem
slide-50
SLIDE 50

(3)

  • email ?
  • mail = ActionMailer::Base.deliveries.last
  • config.before(:each)

{ ActionMailer::Base.deliveries.clear }

  • https://github.com/email-spec/email-

spec/

slide-51
SLIDE 51

(4)

  • ?
  • spec/fixtures/
  • File.new(Rails.root + ‘spec/fixtures/

foobar.png') paperclip Photo.create(:description => "Test", :attachment => File.new(Rails.root + ‘spec/fixtures/ac_logo.png'))

  • feature spec capybara attach_file

http://www.rubydoc.info/github/jnicklas/capybara/ master/Capybara%2FNode%2FActions%3Aattach_file

slide-52
SLIDE 52

(5)

  • devise https://github.com/plataformatec/

devise Test helpers

config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :view config.include Devise::Test::IntegrationHelpers, type: :feature

slide-53
SLIDE 53

(6)

  • sidekiq ?
  • http://api.rubyonrails.org/classes/

ActiveJob/TestHelper.html#method-i- perform_enqueued_jobs

  • config.include ActiveJob::TestHelper
  • enqueue job

perform_enqueued_jobs { … }

slide-54
SLIDE 54

(7)

  • after_commit ?
  • unit test transaction after_commit
  • https://github.com/grosser/test_after_commit
  • database_cleanner truncation transaction
  • trigger
  • http://mytrile.github.io/blog/2013/03/28/testing-after-

commit-in-rspec/

  • Rails 5.0 workaround
slide-55
SLIDE 55

(8)

  • rake ?
  • model class

method

  • model spec
slide-56
SLIDE 56

(9)

  • ?
  • Test double (?)
  • CPU
  • https://github.com/grosser/parallel_tests
  • CI concurrent build
slide-57
SLIDE 57

T

  • ols
  • shoulda rails matcher
  • database_cleaner DB
  • vcr HTTP response 3-party

service

  • simplecov
  • cucumber
  • CI (continuous integration (CI)
slide-58
SLIDE 58

simplecov

slide-59
SLIDE 59

?

slide-60
SLIDE 60

Coverage !

slide-61
SLIDE 61

CI

  • 3-party service
  • https://www.codeship.io
  • https://circleci.com/
  • https://travis-ci.org/
  • build your own
  • Jenkins
slide-62
SLIDE 62

?

slide-63
SLIDE 63

Spec

  • Rspec spec
  • Custom Matcher
slide-64
SLIDE 64

Test Pyramid

http://watirmelon.com/2012/01/31/introducing-the-software-testing-ice-cream-cone/ http://martinfowler.com/bliki/TestPyramid.html

developer QA XD salesforce ?

slide-65
SLIDE 65
slide-66
SLIDE 66

https://www.quora.com/What-is-the-best-way-to-test-the-web-application

What is the best way to test the web application?

slide-67
SLIDE 67

Why?

  • low-level

debug

  • high-level

debug

  • view
  • developer

QA

slide-68
SLIDE 68
  • bug failures

trace

( Mocks )

https://thoughtbot.com/upcase/videos/testing-antipatterns

slide-69
SLIDE 69

Isolation

  • it

Expectation

  • one failure one problem 


trace

slide-70
SLIDE 70
  • describe "#amount" do

it "should discount" do user.vip = true

  • rder.amount.should == 900

user.vip = false

  • rder.amount.should == 1000

end end

slide-71
SLIDE 71

context

describe "#amount" do context "when user is vip" do it "should discount ten percent" do user.vip = true

  • rder.amount.should == 900

end end context "when user is not vip" do it "should discount five percent" do user.vip = false

  • rder.amount.should == 1000

end end end

slide-72
SLIDE 72
  • Private methods
slide-73
SLIDE 73
  • Private methods

class Order def amount if @user.vip? self.caculate_amount_for_vip else self.caculate_amount_for_non_vip end end private def caculate_amount_for_vip # ... end def caculate_amount_for_non_vip # ... end end

slide-74
SLIDE 74
  • it "should discount for vip" do

@order.send(:caculate_amount_for_vip).should == 900 end it "should discount for non vip" do @order.send(:caculate_amount_for_vip).should == 1000 end

slide-75
SLIDE 75

Private methods

  • public

private/protect methods

  • private
  • public
  • Public methods

Private/Protected

slide-76
SLIDE 76
  • describe User do

describe '.search' do it 'searches Google for the given query' do HTTParty.should_receive(:get).with('http://www.google.com', :query => { :q => 'foo' } ).and_return([]) User.search query end end end

  • HTTP
slide-77
SLIDE 77
  • describe User do

describe '.search' do it 'searches for the given query' do User.searcher = Searcher Searcher.should_receive(:search).with('foo').and_return([]) User.search query end end end

slide-78
SLIDE 78
  • class User < ActiveRecord::Base

class_attribute :searcher def self.search(query) searcher.search query end end class Searcher def self.search(keyword, options={}) HTTParty.get(keyword, options) end end

slide-79
SLIDE 79

?

slide-80
SLIDE 80
slide-81
SLIDE 81

TATFT

test all the f**king time

slide-82
SLIDE 82
slide-83
SLIDE 83

bug

  • bug
slide-84
SLIDE 84

bug

slide-85
SLIDE 85

DHH Way

  • 100% test coverage
  • Code-to-test 1:2 1:3
  • 1/3
  • Active Record associations, validations, or scopes.
  • ( Unit Test )
  • Cucumber
  • Specification by Example
  • TDD (DHH 20% TDD)
  • Model DB Fixtures
  • Controller
  • Views system/browser testing

https://signalvnoise.com/posts/3159-testing-like-the-tsa http://david.heinemeierhansson.com/2014/test-induced-design-damage.html

slide-86
SLIDE 86

? coverage?

slide-87
SLIDE 87

code review

slide-88
SLIDE 88

pair programming

slide-89
SLIDE 89
  • EPIC FAIL
slide-90
SLIDE 90
slide-91
SLIDE 91
  • Unit Test
slide-92
SLIDE 92

Reference:

  • http://guides.rubyonrails.org/testing.html
  • https://github.com/eliotsykes/rspec-rails-examples#api-request-specs-docs--

helpers

  • The RSpec Book
  • The Rails 3 Way
  • Foundation Rails 2
  • xUnit Test Patterns
  • everyday Rails Testing with RSpec
  • http://betterspecs.org/
  • http://pure-rspec-rubynation.heroku.com/
  • http://jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/
  • http://martinfowler.com/articles/mocksArentStubs.html
  • http://blog.rubybestpractices.com/posts/gregory/034-issue-5-testing-antipatterns.html
  • http://blog.carbonfive.com/2011/02/11/better-mocking-in-ruby/
slide-93
SLIDE 93

Thanks.