OhSnap

OhSnap is a Ruby gem for snapshot testing with helpers for Minitest and RSpec to name snapshot files after the tests that create them. It is built to be asserted via the equality comparator so you can use any assertion that you like that is based on it.

Features

  • Compare snapshots with #==: No special assertions are needed; just use equality syntax.
  • Split in multiple files: Stores snapshots in separate files for easy inspection.
  • Automatic snapshot naming for Minitest and RSpec: Automatically names snapshot files based on the test name.

Installation

Add this line to your application's Gemfile:

gem 'oh_snap'

and then execute:

bundle install

Or install it yourself as:

gem install oh_snap

Usage

Use the OhSnap::Helper to set up your snapshot registry on every test run:

require 'oh_snap/helper'

class MyTest < Test::Unit::TestCase
  include OhSnap::Helper

  def startup
    super
    setup_snapshots(path: File.dirname(__FILE__))
  end

  def test_a_snapshot_matches
    assert_equal snapshot(name: 'snapshot_name'), 'some value'
  end

  def shutdown
    save_snapshots
    super
  end
end

You are responsible for specifying the base location for the __snapshots__ directory in the path: argument and naming your snapshots passing name: in each call to #snapshot.

Bear in mind that order is important when comparing snapshots to values:

# Bad
assert 'something' == snapshot
assert_equal 'something', snapshot

# Good
assert snapshot == 'something'
assert_equal snapshot, 'something'

If you really do not care about choosing your assertion, use the snapshot_matches helper method included with OhSnap::Helper:

assert snapshot_matches('something')
expect(snapshot_matches('something')).to be true

Minitest and RSpec

Helpers for RSpec and Minitest make things easier because they name the snapshot file automagically based on the test name:

Minitest

In your test file:

require 'minitest/autorun'
require 'oh_snap/minitest'

class MyTest < Minitest::Test
  include OhSnap::Minitest

  def test_example
    result = perform_complex_operation
    assert snapshot == result
  end

    def test_example_with_assert_equal
    result = perform_complex_operation
    assert_equal snapshot, result
  end

  def test_example_with_matcher
    result = perform_complex_operation
    assert snapshot_matches(result)
  end

  def test_custom_named_snapshot
    result = perform_another_operation
    assert_equal snapshot(name: 'custom_snapshot_name'), result
  end

  def test_custom_named_snapshot_with_matcher
    result = perform_another_operation
    assert snapshot_matches(result, name: 'custom_snapshot_name')
  end
end

RSpec

In your test file:

require 'oh_snap/rspec'

RSpec.configure do |config|
  config.include OhSnap::RSpec
end

RSpec.describe 'My feature' do
  it 'produces the correct output' do
    result = perform_complex_operation
    expect(snapshot).to eq result
  end

  it 'also produces the correct output' do
    result = perform_complex_operation
    expect(snapshot_matches(result)).to be true
  end

  it 'produces the correct output with custom snapshot name' do
    result = perform_another_operation
    expect(snapshot(name: 'custom_snapshot_name')).to eq result
  end

  it 'also produces the correct output with custom snapshot name' do
    result = perform_another_operation
    expect(snapshot_matches(result, name: 'custom_snapshot_name')).to be true
  end
end

In both cases, snapshots will be saved in the __snapshots__ directory adjacent to the test file.

Updating Snapshots

To update existing snapshots, set the UPDATE_SNAPSHOTS environment variable:

UPDATE_SNAPSHOTS=1 bundle exec rake test

This will regenerate snapshots for tests that fail due to mismatches.

Notice that setting any value for UPDATE_SNAPSHOTS will be interpreted as setting the write mode as true. If you do not want to update snapshots, do not set UPDATE_SNAPSHOTS at all. The latter should be the case in your CI environment.

Configuration

Global configuration can be set via OhSnap.config:

OhSnap.config.tap do |config|
  config.serializer = OhSnap::IdentitySerializer # Should inherit from OhSnap::BaseSerializer and implement #serialize.
  config.snapshots_dirname = '__snapshots__' # Sets the name for the directory used to store snapshots.
  config.writable = false # Sets the write/update mode. `true` if the UPDATE_SNAPSHOTS environment variable is set.
end

Write your own serializer

Using IdentitySerializer forces you to pass only stringified values to save in snapshots. You can write your own serializer inheriting from OhSnap::BaseSerializer and implementing the #serialize method to return a string.

require 'json'

class OhSnap::JSONSerializer < OhSnap::BaseSerializer
  def serialize(value)
    JSON.dump(value)
  end
end

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/migl/oh_snap.

License

The gem is available as open source under the terms of the MIT License.