Slide 1

Slide 1 text

@JonRowe RSPEC + RAILS Without rspec-rails

Slide 2

Slide 2 text

History 2179 commits, 194 people since 2009 Thin wrapper over Rails helpers

Slide 3

Slide 3 text

Why Rails is integrated Rails is opinionated Rails is slow(er) to boot

Slide 4

Slide 4 text

How Load components of Rails as needed Change how we test

Slide 5

Slide 5 text

How PORO Specs Model Specs Controller Specs Acceptance / Feature Specs Other Specs

Slide 6

Slide 6 text

Autoloading To autoload or not to autoload. `require` works… but can be painful Rails constant lookup can be surprising

Slide 7

Slide 7 text

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!

Slide 8

Slide 8 text

PORO Specs Plain old Ruby specs No change!

Slide 9

Slide 9 text

Model Specs Need to load the database beforehand Don’t use `describe Model`

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

# 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

Slide 13

Slide 13 text

Controller Specs Don’t

Slide 14

Slide 14 text

Controller Specs

Slide 15

Slide 15 text

Controller Specs Controller specs are already fullstack Too hard to isolate them Refactor logic to services and test or… Write acceptance tests

Slide 16

Slide 16 text

Acceptance Specs Are still (as slow as) Rails Partition from your suite

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Other Services / Presenters / Forms / ETC As long as they’re PORO(ish), work fine Mailers work with `email_spec`

Slide 21

Slide 21 text

Other View / Request / Etc Not SRP, write acceptance specs or… Use `rspec-rails`

Slide 22

Slide 22 text

THANKS @JONROWE

Slide 23

Slide 23 text

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