Bogus (Ruby)
Bogus is a Ruby API library used for minimizing risks involved in isolated unit testing. It was initially released in July 2012 by rubygems.org.[1] Through Bogus, a piece of code can be tested in a fast and safe manner, without any actual integration with external programs. Bogus cannot mock or stub methods not present in the required external environment.
Designed by | Adam Pohorecki, Paweł Pierzchała, Piotr Szotkowski, Marek Nowak |
---|---|
First appeared | 30 July 2012 |
Stable release | 0.1.6
/ 2 January 2015 |
Implementation language | Ruby |
License | The MIT License (MIT) Copyright (c) 2012-2013 Adam Pohorecki |
Website | https://rubygems.org/gems/bogus |
Features
Ruby provides various features to achieve the required testing framework.
Fakes
Fakes are lightweight objects that mock actual objects' interface. In order to test a class in isolation, usually some test doubles or anonymous objects are used in place of integrated classes with required methods stubbed in it. But there is a problem with this approach, If the class is changed in between, those changes are not reflected in mock objects and tests run without any integration exceptions. Fakes resolve this problem as they will have exact interface of real collaborator and will raise an exception whenever the actual class is modified.
Fakes can be implemented using the following methodologies.
Faking existing classes
A fake is created by calling fake method:
Syntax:
fake(fake_name, options) { ClassToCopy }
"fake_name" is the name of the created fake and can be used to identify the fake while using contract tests. if it is omitted then an anonymous fake is created. A fake provides options to return an instance or a copy of class depending on the requirement. Fakes can be applied to stub methods as well.
Global configuration
Fakes avoid the need of stubbing and eliminate much of configuration things like (as: :class
/ as: :instance
[2]). Regardless, this type of configuration should be added to fake definitions, and the more collaborators one has, there will be more duplication. Bogus deals with this problem by introducing DSL [3] to configure fakes in a single place, thus unifying its use in all the tests. Stubbed methods can be overridden using fake macros or fake helper functions.
Nameless test doubles
Anonymous test doubles can be used as intermediate steps while migrating objects from another testing library. They help in observing methods without mocking them. Methods can be stubbed:
- with any number of arguments
- in an initialize method
- inline
- to invoke arbitrary methods
Replacing classes
Usually dependency injections are used to make the composition of classes easy and make the code more modular. Replacement of classes with fakes avoids this requirement. Different fake names can be given to created fake classes.
Duck type faking
A system may have multiple classes implementing a single function. This complicates the selection of an implementation for that particular scenario. Bogus solves this problem by creating a duck type for classes.
Syntax to create a duck type:
make_duck(Class 1, Class 2, Class 3)
Bogus simplifies it further by creating duck types automatically when more than one class are returned from fake. Indeed, it does it in global configuration as well.
Contract tests
In order to verify that exception handling is done as expected in the fake stub, a sample test should be written for the mock object. This test is not written by Bogus, but it reminds the developer to do so.
verify_contract[4](object) method records all the interceptions made with the real class, and fails the test if any scenario recorded on a fake object that has been forgotten to be tested.
Whenever an object is mocked, stubbed or spied, a contract is specified on input/output parameters. Bogus automatically verifies whether the contract is satisfied or not. They fail when mocked or stubbed methods are not called on real objects. Contract tests not only check whether correct number of parameters are passed but also makes sure that the object is tested with right arguments.
Contract verification can be identified correctly only when passed block of code or arguments are idempotent[5] and without any side effects.
Stubbing
In addition to fakes, bogus allows stubs in the cases where the inversion of control principle is not used. They follow two conditions:
- The method that is being stubbed should exist.
- The passing parameters need to be consistent. It retains the method signature.
The syntax is similar to that of ruby stubbing:[6]
# RR syntax
stub(object).method_name { return_value }
# Bogus syntax
stub(object).method_name(any_args) { return_value }
The main difference between fakes and stubbing is that in fakes, method call can be verified before stubbing. But in stubbing, to verify that a method call has occurred, stubbing beforehand is necessary.
Safe mocking
Mocks are used to test an object in isolation. These help to find out if there is proper communication between the object and its collaborators. Mocks are like stubs in the way that the object and the method being mocked need to exist.
Syntax:[7]
mock(object).method_name(*args) { return_value }
Spies
When the code is being tested, the message transmission and reception is a vital part. This is verified using spies. They are used to specify what needs to happen in the ideal case. Spies verify that a method is called before stubbing it.
Argument matchers
Argument matchers are helpful when the details of the passing arguments are not as important as knowing if the method is being called or not. Instead of the actual arguments, some matchers like wildcard entries or regular expressions can be used.[8][9]
Configuration options
Bogus configuration syntax:[10]
Bogus.configure do |c|
c.search_modules = [Object]
c.fake_ar_attributes = true
end
Search_modules
By default, Bogus does not maintain a namespace. Therefore, during the search for a particular class(to resolve a class name), the search is performed on all of the identifiers. In order to narrow down the search space, Search_modules can be customized to include a particular module of classes. For example:[11]
Bogus.configure do |c|
c.search_modules << Bar
end
Here, 'Bar' is a module which contains a set of classes where the search has to be performed.
Fake_ar_modules
This feature deals with the ActiveRecord::Base[12] class. Here, the Active Record is a module and Base is one of its classes. Active Record's attributes are not explicit. Their modification is done in the database directly. The Base class is a special class that has methods it responds to but doesn't explicitly mention as its own methods. Therefore, when such a method is faked, it raises an error because the method does not exist for the class. For example,[13]
class BlogPost < ActiveRecord::Base
end
blog_post = BlogPost.new
blog_post.respond_to?(:name) # => true
blog_post.method(:name) # raises NameError
To avoid this problem, fake_ar_modules is set to true. Then fakes can be created with no error.
RSpec Mocks vs Bogus
Bogus isolates objects while unit testing i.e., it does not require any information of classes or objects involved in testing object method, whereas Rspec Mocks require additional information regarding objects used in testing object. One major advantage of using bogus over Rspec Mocks is that Bogus provides safe stubbing.
Version history
Version | Highlights |
---|---|
0.1.6 | Cleared tests on Rubinius, RSpec 3 usage |
0.1.5 | Matchers were added, refined features |
0.1.4 | Reorganised code, improved fakes, minitest contract support |
0.1.3 | Minitest support |
0.1.2 | Removed RSpec warning |
0.1.1 | Support for Rubinius and JRuby |
0.1.0 | Supports Ruby 2.0, stubbing on fakes, improved spying |
0.0.4 | Improved mocks |
0.0.3 | Global fakes, improved stub syntax |
0.0.2 | Anonymous fakes, improved stub method calls |
0.0.1 | Fakes, stubs, mocks, spies |
References
- "RubyGems.org - your community gem host". rubygems.org.
- "Global fake configuration - Fakes - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-13.
- "What's a Ruby DSL and what isn't?". InfoQ. Retrieved 2017-02-10.
- "Contract Tests - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-13.
- "What are idempotent and/or safe methods? - The RESTful cookbook". restcookbook.com. Retrieved 2017-02-13.
- "Safe Stubbing - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-11.
- "Safe mocking - Safe Stubbing - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-11.
- "Bogus: A Brief Introduction".
- "Argument matchers - Safe Stubbing - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-12.
- "Configuration Options - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-11.
- "search_modules - Configuration Options - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-11.
- "Class: ActiveRecord::Base — Documentation for rails (3.1.1)". www.rubydoc.info.
- "fake_ar_attributes - Configuration Options - Bogus - bogus - Relish". www.relishapp.com. Retrieved 2017-02-11.
Sources
- https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/getting-started
- https://github.com/psyho/bogus
- https://github.com/groyoh/bogus
- https://www.ruby-toolbox.com/projects/bogus
- https://packages.debian.org/sid/ruby/ruby-bogus
- http://gunpowderlabs.com/blog/write-safer-tests-with-bogus/
- https://coderwall.com/p/d1dksg/safe-stubbing-with-bogus
- http://bogus-wrug-2013-07.herokuapp.com/#59