All Projects → jbodah → spy_rb

jbodah / spy_rb

Licence: other
🔍 Transparent Test Spies for Ruby

Programming Languages

ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to spy rb

bokor
Bokor is a simple, Record and Playback Mock Server written in Node.js, utilized for Service Virtualization.
Stars: ✭ 24 (-14.29%)
Mutual labels:  mocking
mockfn
A mocking library for Clojure.
Stars: ✭ 18 (-35.71%)
Mutual labels:  mocking
dummyhttp
Super simple HTTP server that replies a fixed body with a fixed response code
Stars: ✭ 25 (-10.71%)
Mutual labels:  mocking
chrome-extension-mocker
The most convenient tool to mock requests for axios, with built-in Chrome extension support.
Stars: ✭ 37 (+32.14%)
Mutual labels:  mocking
laika
Log, test, intercept and modify Apollo Client's operations
Stars: ✭ 99 (+253.57%)
Mutual labels:  mocking
open-api-mocker
A mock server based in OpenAPI Specification
Stars: ✭ 58 (+107.14%)
Mutual labels:  mocking
rest-cljer
A clojure wrapper for the rest driver library
Stars: ✭ 36 (+28.57%)
Mutual labels:  mocking
jaymock-cli
Mock an API and generate fake JSON test data, right from the terminal.
Stars: ✭ 13 (-53.57%)
Mutual labels:  mocking
Assume
Easy Response Mocking for Retrofit using annotations
Stars: ✭ 25 (-10.71%)
Mutual labels:  mocking
CNeptune
CNeptune improve productivity & efficiency by urbanize .net module with meta-code to lay foundation for frameworks
Stars: ✭ 30 (+7.14%)
Mutual labels:  mocking
MockAlamofire
A simple example showing how to override the URLProtocol to return mock data on Alamofire responses. Helpful if you are looking for a simple way to mock an Alamofire response, with out any additional dependencies.
Stars: ✭ 22 (-21.43%)
Mutual labels:  mocking
mock-alchemy
SQLAlchemy mock helpers.
Stars: ✭ 44 (+57.14%)
Mutual labels:  mocking
Mockaco
🐵 HTTP mock server, useful to stub services and simulate dynamic API responses, leveraging ASP.NET Core features, built-in fake data generation and pure C# scripting
Stars: ✭ 213 (+660.71%)
Mutual labels:  mocking
wiremock
A tool for mocking HTTP services
Stars: ✭ 5,239 (+18610.71%)
Mutual labels:  mocking
umock-c
A pure C mocking library
Stars: ✭ 29 (+3.57%)
Mutual labels:  mocking
dexopener
An Android library that provides the ability to mock your final classes on Android devices.
Stars: ✭ 112 (+300%)
Mutual labels:  mocking
FluentSimulator
A fluent syntax .NET REST/HTTP API simulator for automated unit and UI testing.
Stars: ✭ 23 (-17.86%)
Mutual labels:  mocking
better-mock
Forked from Mockjs, Generate random data & Intercept ajax request. Support miniprogram.
Stars: ✭ 140 (+400%)
Mutual labels:  mocking
php-test-generator
Generate test cases for existing PHP files
Stars: ✭ 47 (+67.86%)
Mutual labels:  mocking
jmockit2
The evolution of JMockit APIs: smaller, simpler, safer, and still powerful
Stars: ✭ 33 (+17.86%)
Mutual labels:  mocking

Spy

Travis Status Coverage Status Code Climate Gem Version

Transparent Test Spies for Ruby

Description

Mocking frameworks work by stubbing out functionality. Spy works by listening in on functionality and allowing it to run in the background. Spy is designed to be lightweight and work alongside Mocking frameworks instead of trying to replace them entirely.

Why Spy?

  • Less intrusive than mocking
  • Allows you to test message passing without relying on stubbing
  • Great for testing recursive methods or methods with side effects (e.g. test that something is cached and only hits the database once on the intitial call)
  • Works for Ruby 2.x
  • Small and simple
  • Strong test coverage
  • No alias_method pollution
  • No dependencies!

Install

gem install spy_rb

Usage

Spy::API

Spy::API defines the top-level interface for creating spies and for interacting with them globally.

You can use it to create spies in a variety of ways. For these example we'll use the Fruit class because, seriously, who doesn't love fruit:

class Fruit
  def eat(adj)
    puts "you take a bite #{adj}"
  end
end

require 'spy'

# Spy on singleton or bound methods
fruit = Fruit.new
s = Spy.on(fruit, :to_s)
fruit.to_s
s.call_count
#=> 1

s = Spy.on(Fruit, :to_s)
Fruit.to_s
s.call_count
#=> 1

# Spy on instance methods
s = Spy.on_any_instance(Fruit, :to_s)
apple = Fruit.new
apple.to_s
orange = Fruit.new
orange.to_s
s.call_count
#=> 2

# Spied methods respect visibility
Object.private_methods.include?(:fork)
#=> true
Spy.on(Object, :fork)
Object.fork
#=> NoMethodError: private method `fork' called for Object:Class

# Spy will let you know if you're doing something wrong too
Spy.on(Object, :doesnt_exist)
#=> NameError: undefined method `doesnt_exist' for class `Class'

Spy.on(Fruit, :to_s)
=> #<Spy::Instance:0x007feb55affe18 @spied=Fruit, @original=#<Method: Class(Module)#to_s>, @visibility=:public, @conditional_filters=[], @before_callbacks=[], @after_callbacks=[], @around_procs=[], @call_history=[], @strategy=#<Spy::Instance::Strategy::Intercept:0x007feb55affc38 @spy=#<Spy::Instance:0x007feb55affe18 ...>, @intercept_target=#<Class:Fruit>>>

Spy.on(Fruit, :to_s)
#=> Spy::Errors::AlreadySpiedError: Spy::Errors::AlreadySpiedError

# Spy on all of the methods of an object (also see Spy.on_class)
s = Spy.on_object(fruit)
fruit.to_s
s.call_count
#=> 1
s[:to_s].call_count
#=> 1

When you're all finished you'll want to restore your methods to clean up the spies:

# Restore singleton/bound method
s = Spy.on(Object, :to_s)
Spy.restore(Object, :to_s)

# Restore instance method
s = Spy.on_any_instance(Object, :to_s)
Spy.restore(Object, :to_s, :instance_method)

# Restore method_missing-style delegation
Spy.restore(Object, :message, :dynamic_delegation)

# Global restore
s = Spy.on(Object, :to_s)
Spy.restore(:all)

If you're using spy_rb in the context of a test suite, you may want to patch a Spy.restore(:all) into your teardowns:

class ActiveSupport::TestCase
  teardown do
    Spy.restore(:all)
  end
end

Spy::Instance

Once you've created a spy instance, then there are a variety of ways to interact with that spy. See Spy::Instance for the full list of supported methods.

Spy::Instance#call_count will tell you how many times the spied method was called:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
fruit.eat(:slowly)
spy.call_count
#=> 1

fruit.eat(:quickly)
spy.call_count
#=> 2

Spy::Instance#when lets you specify conditions as to when the spy should track calls:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
spy.when {|method_call| method_call.args.first == :quickly}
fruit.eat(:slowly)
spy.call_count
#=> 0

fruit.eat(:quickly)
spy.call_count
#=> 1

Spy::Instance#before and Spy::Instance#after give you callbacks for your spy:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
spy.before { puts 'you wash your hands' }
spy.after { puts 'you rejoice in your triumph' }
fruit.eat(:happily)
#=> you wash your hands
#=> you take a bite happily
#=> you rejoice in your triumph

# #before and #after can both accept arguments just like #when

Spy::Instance#wrap allows you to do so more complex things. Be sure to call the original block though! You don't have to worry about passing args to the original. Those are wrapped up for you; you just need to #call it.

require 'benchmark'
fruit = Fruit.new
spy = Spy.on(fruit, :eat)
spy.wrap do |method_call, &original|
  puts Benchmark.measure { original.call }
end
fruit.eat(:hungrily)
#=> you take a bite hungrily
#=> 0.000000   0.000000   0.000000 (  0.000039)

Spy::Instance#instead lets you emulate stubbing:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
spy.instead { puts "taking a nap" }
fruit.eat(:hungrily)
#=> taking a nap

Spy::Instance#call_history keeps track of all of your calls for you. It returns a list of Spy::MethodCall objects which give you even more rich features:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
fruit.eat(:like_a_boss)
fruit.eat(:on_a_boat)
spy.call_history
#=> [
  #<Spy::MethodCall:0x007fd1db0dc6e0 @replayer=#<Proc:0x007fd1db0dc730@/Users/Bodah/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/spy_rb-0.3.0/lib/spy/instance/api/internal.rb:60>, @name=:eat, @receiver=#<Fruit:0x007fd1db0efdd0>, @args=[:like_a_boss], @result=nil>,
  #<Spy::MethodCall:0x007fd1db033c70 @replayer=#<Proc:0x007fd1db033cc0@/Users/Bodah/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/spy_rb-0.3.0/lib/spy/instance/api/internal.rb:60>, @name=:eat, @receiver=#<Fruit:0x007fd1db0efdd0>, @args=[:on_a_boat], @result=nil>
]

Spy::MethodCall

Spy::MethodCall has a bunch of useful attributes like #receiver, #args, #caller, #block, #name, and #result. Right now Spy::MethodCall does not deep copy args or results, so be careful!

Spy::MethodCall also has the experimental feature #replay which can be used interactively for debugging:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
fruit.eat(:quickly)
#=> you take a bite quickly

spy.call_history[0].replay
#=> you take a bite quickly

spy.call_count
#=> 1

Additionally, if you're adventurous you can give Spy::Instance#replay_all a shot:

fruit = Fruit.new
spy = Spy.on(fruit, :eat)
fruit.eat(:quickly)
#=> you take a bite quickly

fruit.eat(:slowly)
#=> you take a bite slowly

spy.call_count
#=> 2

spy.replay_all
#=> you take a bite quickly
#=> you take a bite slowly

spy.call_count
#=> 2

Deploying (note to self)

rake full_deploy TO=0.2.1
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].