エラーが発生しないテスト

not_to raise_error

RSpec.describe "#to_s" do
  it "does not raise" do
    expect { Object.new.to_s }.not_to raise_error
  end
end

raise_error matcher - Built in matchers - RSpec Expectations - RSpec - Relish

モックでエラーを発生させる

and_raise

RSpec.describe "Making it raise an error" do
  it "raises the provided exception" do
    dbl = double
    allow(dbl).to receive(:foo).and_raise("boom")
    dbl.foo
  end
end

Raising an error - Configuring responses - RSpec Mocks - RSpec - Relish

モックの引数のチェック

expect(...).to receive(...).with()

RSpec.describe "Using a RSpec matcher" do
  let(:dbl) { double }
  before { expect(dbl).to receive(:foo).with(a_collection_containing_exactly(1, 2)) }

  it "passes when the args match" do
    dbl.foo([2, 1])
  end

  it "fails when the args do not match" do
    dbl.foo([1, 3])
  end
end

Matching arguments - Setting constraints - RSpec Mocks - RSpec - Relish

メソッドが実行された回数

expect(...).to have_received(...).exactly(...).times

class Account
  attr_accessor :logger

  def open
    logger.account_opened
  end
end

describe Account do
  context "when opened" do
    it "logger#account_opened was called once" do
      logger = double("logger")
      account = Account.new
      account.logger = logger

      logger.should_receive(:account_opened).exactly(3).times

      account.open
      account.open
      account.open
    end
  end
end

receive counts - Message expectations - RSpec Mocks - RSpec - Relish

一つのit内で複数のexpectを実行

aggregate_failures

require 'client'

RSpec.describe Client do
  after do
    # this should be appended to failure list
    expect(false).to be(true), "after hook failure"
  end

  around do |ex|
    ex.run
    # this should also be appended to failure list
    expect(false).to be(true), "around hook failure"
  end

  it "returns a successful response" do
    response = Client.make_request

    aggregate_failures "testing response" do
      expect(response.status).to eq(200)
      expect(response.headers).to include("Content-Type" => "application/json")
      expect(response.body).to eq('{"message":"Success"}')
    end
  end
end
RSpec/MultipleExpectations の対策

Aggregating Failures - Expectation framework integration - RSpec Core - RSpec - Relish

mockの引数などをspyする順番

allow(...).to receive(...) -> subject -> expect(...).to have_received(...) の順番で実行する

RSpec.describe "have_received" do
  it "passes when the expectation is met" do
    allow(Invitation).to receive(:deliver)
    Invitation.deliver
    expect(Invitation).to have_received(:deliver)
  end
end

誤り

RSpec.describe "have_received" do
  it "passes when the expectation is met" do
    allow(Invitation).to receive(:deliver)
    expect(Invitation).to receive(:deliver).once
    Invitation.deliver
  end
end
RSpec/MessageSpies の対策

Spies - Basics - RSpec Mocks - RSpec - Relish