$30 off During Our Annual Pro Sale. View Details »

RSpec + Rails Without rspec-rails @ RoRoSyd November 2015

Jon Rowe
November 10, 2015

RSpec + Rails Without rspec-rails @ RoRoSyd November 2015

Jon Rowe

November 10, 2015
Tweet

More Decks by Jon Rowe

Other Decks in Technology

Transcript

  1. @JonRowe
    RSPEC + RAILS
    Without rspec-rails

    View Slide

  2. History
    2179 commits, 194 people since 2009

    Thin wrapper over Rails helpers

    View Slide

  3. Why
    Rails is integrated

    Rails is opinionated

    Rails is slow(er) to boot

    View Slide

  4. How
    Load components of Rails as needed

    Change how we test

    View Slide

  5. How
    PORO Specs

    Model Specs

    Controller Specs

    Acceptance / Feature Specs

    Other Specs

    View Slide

  6. Autoloading
    To autoload or not to autoload.

    `require` works… but can be painful

    Rails constant lookup can be surprising

    View Slide

  7. require 'bundler/setup'
    gem_paths =
    Dir[
    File.join Bundler.bundle_path,
    “/gems/**/lib”
    ]
    app_paths =
    Dir[
    File.expand_path(‘./lib/**'),
    File.expand_path(‘./app/**')
    ]
    (gem_paths + app_paths).each do |path|
    ActiveSupport::Dependencies
    .autoload_paths << File.expand_path(path)
    end
    ActiveSupport::Dependencies.hook!

    View Slide

  8. PORO Specs
    Plain old Ruby specs

    No change!

    View Slide

  9. Model Specs
    Need to load the database beforehand

    Don’t use `describe Model`

    View Slide

  10. module Support::Database
    def self.setup!
    return true if defined?(@connection)
    require ‘yaml’; require ‘active_record'
    config = YAML.load(ERB.new(
    IO.read(‘config/database.yml')
    ).result)
    @connection = ActiveRecord::Base
    .establish_connection(config[‘test'])
    ActiveRecord::Base
    .raise_in_transactional_callbacks = true
    true
    end
    end
    RSpec.configuration.before :context, :db do
    Support::Database.setup!
    end

    View Slide

  11. RSpec.describe 'MyModel', :db do
    let(:models) do
    (1..6).map { |i| MyModel.create! hidden: false }
    end
    describe '.active' do
    it 'returns all active models' do
    models[0..2].each do |model|
    model.update_attributes! hidden: tru
    end
    expect(
    MyModel.active
    ).to match_array models[3..-1]
    end
    end
    end

    View Slide

  12. # For verified doubles with ActiveRecord
    ::RSpec::Mocks.configuration
    .when_declaring_verifying_double do |possible|
    target = possible.target
    if target.respond_to?(:define_attribute_methods)
    possible.target.define_attribute_methods
    end
    end

    View Slide

  13. Controller Specs
    Don’t

    View Slide

  14. Controller Specs

    View Slide

  15. Controller Specs
    Controller specs are already fullstack

    Too hard to isolate them

    Refactor logic to services and test or…

    Write acceptance tests

    View Slide

  16. Acceptance Specs
    Are still (as slow as) Rails

    Partition from your suite

    View Slide

  17. require 'capybara/dsl'
    RSpec.configure do |config|
    config.include Capybara::DSL, :app
    config.before :context, :app do
    unless Capybara.app
    require ‘config/environment.rb’
    Capybara.app = Rails.application
    end
    Capybara.asset_host =
    "http://localhost:#{ENV['PORT'] || '8080'}"
    end
    config.after :example, :app do
    Capybara.reset_sessions!
    end
    end

    View Slide

  18. RSpec.describe 'Downloading a report', :app, :db do
    include Support::AdminHelper
    before do
    sign_in_as a_valid_admin
    end
    context 'when statistics are present' do
    it 'produces a csv' do
    visit "/admin"
    click_link 'Download CSV'
    expect {
    CSV.parse body
    }.to_not raise_error CSV::ParseError
    end
    end
    end

    View Slide

  19. RSpec.configure do |config|
    config.register_ordering :global do |examples|
    acceptance, unit = examples.partition do |ex|
    ex.metadata[:acceptance]
    end
    unit.shuffle + acceptance.shuffle
    end
    end

    View Slide

  20. Other
    Services / Presenters / Forms / ETC

    As long as they’re PORO(ish), work fine

    Mailers work with `email_spec`

    View Slide

  21. Other
    View / Request / Etc

    Not SRP, write acceptance specs or…

    Use `rspec-rails`

    View Slide

  22. THANKS
    @JONROWE

    View Slide

  23. CODE
    https://gist.github.com/JonRowe/867423bab8201b1f852b

    View Slide