Search code examples
ruby-on-railsrspec-rails

RSpec "count" change.by?


So I was looking at: https://rubyplus.com/articles/1491-Basic-TDD-in-Rails-Writing-Validation-Tests-for-the-Model

Just seeing techniques of testing and I saw this:

require 'rails_helper'

describe Article, type: :model do
  it 'is valid if title and description fields have value' do
    expect do
      article = Article.new(title: 'test', description: 'test')
      article.save
    end.to change{Article.count}.by(1)
  end
end

Specifically the last line: end.to change{Article.count}.by(1). From reading https://relishapp.com/rspec/rspec-expectations/v/3-7/docs/built-in-matchers/change-matcher

It says specifically:

The change matcher is used to specify that a block of code changes some mutable state. You can specify what will change using either of two forms:

Which makes sense. But were testing Article.count in the block of code which isn't actually "doing" anything (The article.save is what actually changed the Article.count so how exactly does this work? Does the test take a a look at whats in the block of code before it's ran and "prerun" it...the compare the .by(1) after?

Thanks


Solution

  • There are two blocks of code being executed. The block of code passed to expect, and the block of code passed to change. This is what's really happening, in pseudo-code.

    difference = 1
    initial_count = Article.count
    
    article = Article.new(title: 'test', description: 'test')
    article.save
    
    final_count = Article.count
    
    expect(final_count - initial_count).to eq(difference)
    

    I would refactor your test to be a little easier to follow as this:

    require 'rails_helper'
    
    describe Article, type: :model do
      let(:create_article) { Article.create(title: 'test', description: 'test') }
    
      it 'is valid if title and description fields have value' do
        expect { create_article }.to change { Article.count }.by(1)
      end
    end