Giter Site home page Giter Site logo

approvals's Introduction

Approvals

Build Status Gem Version Code Climate Gemnasium

Approvals are based on the idea of the golden master.

You take a snapshot of an object, and then compare all future versions of the object to the snapshot.

Big hat tip to Llewellyn Falco who developed the approvals concept, as well as the original approvals libraries (.NET, Java, Ruby, PHP, probably others).

See ApprovalTests for videos and additional documentation about the general concept.

Also, check out Herding Code's podcast #117 in which Llewellyn Falco is interviewed about approvals.

Configuration

Approvals.configure do |c|
  c.approvals_path = 'output/goes/here/'
end

The default location for the output files is

approvals/

Usage

Approvals.verify(your_subject, :format => :json)

This will raise an ApprovalError in the case of a failure.

The default writer uses the :to_s method on the subject will be used to generate the output for the received file. For custom complex objects you will need to override :to_s to get helpful output, rather than the default:

#<Object:0x0000010105ea40> # or whatever the object id is

The first time the approval is run, a file will be created with the contents of the subject of your approval:

the_name_of_the_approval.received.txt # or .json, .html, .xml as appropriate

Since you have not yet approved anything, the *.approved file does not exist, and the comparison will fail.

CLI

The gem comes with a command-line tool that makes it easier to manage the *.received.* and *.approved.* files.

The basic usage is:

approvals verify

This goes through each approval failure in turn showing you the diff.

The option --diff or -d configures which difftool to use (for example opendiff, vimdiff, etc). The default value is diff.

The option --ask or -a, which after showing you a diff will offer to approve the received file (move it from *.received.* to *.approved.*.). The default is true. If you set this to false, then nothing happens beyond showing you the diff, and you will need to rename files manually.

Workflow Using VimDiff

I have the following mapped to <leader>v in my .vimrc file:

map <leader>v :!approvals verify -d vimdiff -a<cr>

I tend to run my tests from within vim with an on-the-fly mapping:

:map Q :wa <Bar> :!ruby path/to/test_file.rb<cr>

When I get one or more approval failures, I hit <leader>v. This gives me the vimdiff.

When I've inspected the result, I hit :qa which closes both sides of the diff.

Then I'm asked if I want to approve the received file [yN]. If there are multiple diffs, this handles each failure in turn.

RSpec

For the moment the only direct integration is with RSpec.

require 'approvals/rspec'

The default directory for output files when using RSpec is

spec/fixtures/approvals/

You can override this:

RSpec.configure do |c|
  c.approvals_path = 'some/other/path'
end

The basic format of the approval is modeled after RSpec's it:

it "works" do
  verify do
    "this is the the thing you want to verify"
  end
end

Naming

When using RSpec, the namer is set for you, using the example's full_description.

Approvals.verify(thing, :name => "the name of your test")

Formatting

You can pass a format for your output before it gets written to the file. At the moment, only text, xml, html, and json are supported.

Simply add a :format => :text, :format => :xml, :format => :html, or :format => :json option to the example:

page = "<html><head></head><body><h1>ZOMG</h1></body></html>"
Approvals.verify page, :format => :html

data = "{\"beverage\":\"coffee\"}"
Approvals.verify data, :format => :json

In RSpec, it looks like this:

verify :format => :html do
  "<html><head></head><body><h1>ZOMG</h1></body></html>"
end

verify :format => :json do
  "{\"beverage\":\"coffee\"}"
end

Exclude dynamically changed values from json

Approvals.configure do |c|
  c.excluded_json_keys = {
    :id =>/(\A|_)id$/,
    :date => /_at$/
  }
end

It will replace values with placeholders:

{id: 5, created_at: "2013-08-29 13:48:08 -0700"}

=>

{id: "<id>", created_at: "<date>"}

Approving a spec

If the contents of the received file is to your liking, you can approve the file by renaming it.

For an example who's full description is My Spec:

mv my_spec.received.txt my_spec.approved.txt

When you rerun the approval, it should now pass.

Expensive computations

The Executable class allows you to perform expensive operations only when the command to execute it changes.

For example, if you have a SQL query that is very slow, you can create an executable with the actual SQL to be performed.

The first time the spec runs, it will fail, allowing you to inspect the results. If this output looks right, approve the query. The next time the spec is run, it will compare only the actual SQL.

If someone changes the query, then the comparison will fail. Both the previously approved command and the received command will be executed so that you can inspect the difference between the results of the two.

executable = Approvals::Executable.new(subject.slow_sql) do |output|
  # do something on failure
end

Approvals.verify(executable, :options => :here)

RSpec executable

There is a convenience wrapper for RSpec that looks like so:

verify do
  executable(subject.slow_sql) do |command|
     result = ActiveRecord::Base.connection.execute(command)
     # do something to display the result
  end
end

Copyright (c) 2011 Katrina Owen, released under the MIT license

approvals's People

Contributors

badquanta avatar bmarini avatar cpfair avatar hotgazpacho avatar jamonholmgren avatar jeremyruppel avatar jjoos avatar joshwiseman avatar jt-traub avatar kytrinyx avatar liamseanbrady avatar lukewinikates avatar markijbema avatar olivierlacan avatar randycoulman avatar sumeet avatar svarks avatar theotherzach avatar tmock12 avatar willianvdv avatar zph avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

tfe isabella232

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.