• Stars
    star
    362
  • Rank 117,671 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 13 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Obsolete - use verifying doubles in RSpec 3

TOTALLY OBSOLETE

RSpec fire has been completely subsumed by the verifying doubles feature in RSpec 3, which uses the same API. It's not just a port, the RSpec 3 ones are strictly better.

Leaving this here for posterity, but patches will not be accepted and there will be no further releases.

rspec-fire

Checks that stubbed methods on your test double exist, but still allow you to run in isolation when you choose. A failure will be triggered if an invalid method is being stubbed.

Once,
a younger brother came to him,
and asked,

"Father,
I have made and kept my little rule,
my fast,
my meditation and silence.
I strived to cleanse my heart of thoughts,
what more must I do?"

The elder rose up and,
stretched out his hands,
his fingers became like ten lamps ablaze.
He said,

"Why not be totally changed into fire?"

  -- Desert Way, Charlie Hunter

Test doubles are sweet for isolating your unit tests, but we lost something in the translation from typed languages. Ruby doesn't have a compiler that can verify the contracts being mocked out are indeed legit. This hurts larger refactorings, since you can totally change a collaborator --- renaming methods, changing the number of arguments --- and all the mocks that were standing in for it will keep pretending everything is ok.

rspec-fire mitigates that problem, with very little change to your existing coding style.

One solution would be to disallow stubbing of methods that don't exist. This is what mocha does with its Mocha::Configuration.prevent(:stubbing_non_existent_method) option. The downside is, you now have to load the collaborators/dependencies that you are mocking, which kind of defeats the purpose of isolated testing. Not ideal.

Another solution, that rspec-fire adopts, is a more relaxed version that only checks that the methods exist if the doubled class has already been loaded. No checking will happen when running the spec in isolation, but when run in the context of the full app (either as a full spec run or by explicitly preloading collaborators on the command line) a failure will be triggered if an invalid method is being stubbed.

Usage

It's a gem!

gem install rspec-fire

Bit of setup in your spec_helper.rb:

require 'rspec/fire'

RSpec.configure do |config|
  config.include(RSpec::Fire)
end

Specify the class being doubled in your specs:

class User < Struct.new(:notifier)
  def suspend!
    notifier.notify("suspended as")
  end
end

describe User, '#suspend!' do
  it 'sends a notification' do
    # Only this one line differs from how you write specs normally
    notifier = instance_double("EmailNotifier")

    notifier.should_receive(:notify).with("suspended as")

    user = User.new(notifier)
    user.suspend!
  end
end

Run your specs:

# Isolated, will pass always
rspec spec/user_spec.rb

# Will fail if EmailNotifier#notify method is not defined
rspec -Ilib -remail_notifier.rb spec/user_spec.rb

Method presence/absence is checked, and if a with is provided then so is arity.

Protips

Using with an existing Rails project

Create a new file unit_helper.rb that does not require spec_helper.rb. Require this file where needed for isolated tests. To run an isolated spec in the context of your app:

rspec -r./spec/spec_helper.rb spec/unit/my_spec.rb

Using with ActiveRecord

ActiveRecord methods defined implicitly from database columns are not detected. A workaround is to explicitly define the methods you are mocking:

class User < ActiveRecord::Base
  # Explicit column definitions for rspec-fire
  def name; super; end
  def email; super; end
end

Doubling constants

A particularly excellent feature. You can stub out constants using class_double, removing the need to dependency inject collaborators (a technique that can sometimes be cumbersome).

class User
  def suspend!
    EmailNotifier.notify("suspended as")
  end
end

describe User, '#suspend!' do
  it 'sends a notification' do
    # Only this one line differs from how you write specs normally
    notifier = class_double("EmailNotifier").as_stubbed_const

    notifier.should_receive(:notify).with("suspended as")

    user = User.new
    user.suspend!
  end
end

This will probably become the default behaviour once we figure out a better name for it.

Transferring nested constants to doubled constants

When you use class_double to replace a class or module that also acts as a namespace for other classes and constants, your access to these constants is cut off for the duration of the example (since the doubled constant does not automatically have all of the nested constants). The :transfer_nested_constants option is provided to deal with this:

module MyCoolGem
  class Widget
  end
end

# once you do this, you can no longer access MyCoolGem::Widget in your example...
class_double("MyCoolGem")

# ...unless you tell rspec-fire to transfer all nested constants
class_double("MyCoolGem").as_stubbed_const(:transfer_nested_constants => true)

# ...or give it a list of constants to transfer
class_double("MyCoolGem").as_stubbed_const(:transfer_nested_constants => [:Widget])

Doubling class methods

Particularly handy for ActiveRecord finders. Use class_double. If you dig into the code, you'll find you can create subclasses of FireDouble to check for any set of methods.

Preventing Typo'd Constant Names

instance_double("MyClas") will not verify any mocked methods, even when MyClass is loaded, because of the typo in the constant name. There's an option to help prevent these sorts of fat-finger errors:

RSpec::Fire.configure do |config|
  config.verify_constant_names = true
end

When this is set to true, rspec-fire will raise an error when given the name of an undefined constant. You probably only want to set this when running your entire test suite, with all production code loaded. Setting this for an isolated unit test will prevent you from being able to isolate it!

Mocking Done Right (tm)

  • Only mock methods on collaborators, not the class under test.
  • Only mock public methods.

If you can't meet these criteria, your object is probably violating SOLID principles and you should either refactor or use a non-isolated test.

Compatibility

Only RSpec 2.11+ is supported. Tested on all the rubies thanks to Travis CI.

Developing

git clone https://github.com/xaviershay/rspec-fire.git
bundle install
bundle exec rake spec

Patches welcome! I won't merge anything that isn't spec'ed, but I can help you out with that if you are getting stuck.

Still need to support #stub_chain.

Status

rspec-fire is pretty new and not used widely. Yet.

More Repositories

1

enki

A Ruby on Rails blogging app for the fashionable developer. It's better than Mephisto or SimpleLog
Ruby
819
star
2

tufte-graph

a jQuery plugin that makes pretty bar charts
JavaScript
236
star
3

kronic

A dirt simple library for parsing and formatting human readable dates
Ruby
151
star
4

poniard

A dependency injector for Rails for writing clean controllers.
Ruby
112
star
5

db2s3

A rails plugin to backup Mysql to Amazon S3
Ruby
69
star
6

node-amqp

AMQP client for nodejs
JavaScript
52
star
7

consul-client

Ruby client gem for Consul HTTP API.
Ruby
43
star
8

jquery-enumerable

The only fully tested and API consistent enumerable plugin for jQuery (collect, inject and friends)
JavaScript
41
star
9

socialbeat

A proof of concept social crowd beat visualization thing, inspired by Giles Bowkett's RubyFringe talk.
Ruby
21
star
10

classifier

Classifier is a general module to allow Bayesian and other types of classifications.
Ruby
20
star
11

rust-puzzlefighter

A puzzle fighter/swordfighting clone.
Rust
19
star
12

kamel

Create KML files for tasty overlays on google earth and google maps
Ruby
18
star
13

writing

bits of writing
Ruby
16
star
14

haskell-servant-react-auth-example

Haskell
15
star
15

xtdo

Damnit, todo list MY WAY
Ruby
15
star
16

lesstile

Converts text formatted with an exceedingly simple markup language into HTML - perfect for comments on your blog.
Ruby
15
star
17

sandbox

Random bits and pieces
JavaScript
14
star
18

nytimes-slider

The hottest implementation of the nytimes slider you ever did see
JavaScript
14
star
19

dotfiles

My dotfiles
Perl
11
star
20

outage-party

The only incident response plan you'll ever need.
HTML
9
star
21

curlophone-orchestra

Uses bonjour to play a great symphony across many computers
Ruby
9
star
22

dominion-solitaire

An ncurses Dominion implementation with a focus on lightning quick goldfishing.
Ruby
9
star
23

xspec

XSpec is an rspec-inspired testing library that is written in a literate style and designed to be obvious to use, highly modular, and easy to extend. A concept piece.
Ruby
8
star
24

mnemonicker-wordlist

Lists of words.
7
star
25

sheets

Various Lilypond scores I have need for
LilyPond
7
star
26

nested-attributes-demo

An example of how to use nested attributes with rails 3
Ruby
7
star
27

gnucash2ledger

Convert GnuCash files to a format supported by the ledger command line application
Ruby
7
star
28

dm-checked-types

CHECK constraints and validations for your models
Ruby
6
star
29

factorio-layout-designer

JavaScript
6
star
30

buildhawk

Historical information about your build, on a webpage!
Ruby
6
star
31

factorio-hs

Haskell library for working with Factorio data
Haskell
5
star
32

dovin

A proof-assistant for Possibility Storm-style problems for Magic: The Gathering
Haskell
4
star
33

node-amqp-ragel

Node AMQP client that uses a ragel parser in a C extension. Doesn't work.
JavaScript
4
star
34

failtrain

Ruby
4
star
35

jruby-guice

Example app showing how to wire up Ruby classes with Google Guice.
Ruby
4
star
36

vitamin-vcv-modules

Assorted VCV Rack modules
C++
4
star
37

vegeinfo

Most pro-vege websites are shit, so I'm trying to make a better one
JavaScript
4
star
38

mtg-bench

Scala implementation of Magic The Gathering rules, geared towards algorithmic training
Scala
3
star
39

consul-fs

Proof-of-concept fuse adapter for Consul KV store.
Ruby
3
star
40

xtdo-hs

Haskell port of xtdo
Haskell
3
star
41

mnemonicker

An aid for remembering numbers
Ruby
3
star
42

veganmelb

Vegan. In Melbourne. Yeah!
JavaScript
3
star
43

roborabb

present for my bro
Ruby
3
star
44

enki-vim

Publish to enki with VIM. That's pretty neat.
Ruby
3
star
45

project-euler

Literate Haskell solutions to Project Euler
Haskell
3
star
46

nom

command line access to ausnom.com
Ruby
2
star
47

vegan-month

Vegan Month home page
HTML
2
star
48

project-hoff

HOFF
Ruby
2
star
49

blog-v2

Ruby
2
star
50

enki-website

Main page for enki
Ruby
2
star
51

babushka-deps

My deps for babushka
Ruby
2
star
52

reader

venus, with my config
Python
2
star
53

gust

ruby gist clone on top of plain rack
Ruby
2
star
54

singing

I'm learning to sing
2
star
55

card_game

Random Ruby utility libraries for modeling card games
Ruby
2
star
56

mit-advanced-ds

http://courses.csail.mit.edu/6.851/spring12/index.html
Ruby
2
star
57

rhnh

My blog - a fork of enki
Ruby
1
star
58

rack-my-id

An OpenID provider that does very little
Ruby
1
star
59

xaviershay.com

My home page
HTML
1
star
60

mtg-bench-ruby

algorithmic training for MtG
Ruby
1
star
61

clear-menulet

A menulet that tells you when the CLEAR grain exchange is open, with growl notifications
1
star
62

aasm-presentation

Lightning talk for #roro
1
star
63

ori-tracker

C#
1
star
64

cryptopals-solutions

Haskell
1
star
65

snes-game

Assembly
1
star
66

ludum-dare-35

PureScript
1
star
67

ausnom

Nutritional data of foods, as released by FSANZ
Ruby
1
star
68

cibass

It's a CI server which doesn't work yet.
Ruby
1
star
69

mopus-gagnum

Haskell
1
star
70

pacebot

A bot to hang out in slack channels where runners frequent.
JavaScript
1
star
71

licross

crossword game
Haskell
1
star
72

factorio-solver-ui

hella wip
JavaScript
1
star
73

dm-nested-transactions

Adds nested transaction support to DataMapper
Ruby
1
star
74

scribebot

Quick and dirty slackbot for remembering things
Ruby
1
star
75

minisculus

a challenge!
Ruby
1
star
76

pacecalculator

http://pacecalculator.herokuapp.com
JavaScript
1
star
77

hrange

An experimental haskell implementation of the range query language.
Haskell
1
star
78

legendary-bench

Haskell
1
star
79

alien-parade

Aliens on parade! Flocking + gosu.
Ruby
1
star
80

voxels-bench

Rust
1
star