Bogus (Ruby)

From HandWiki

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.

Bogus
Designed byAdam Pohorecki, Paweł Pierzchała, Piotr Szotkowski, Marek Nowak
First appeared30 July 2012
Stable release
0.1.6 / 2 January 2015
Implementation languageRuby
LicenseThe MIT License (MIT) Copyright (c) 2012-2013 Adam Pohorecki
Websitehttps://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:

  1. The method that is being stubbed should exist.
  2. 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

  1. "RubyGems.org - your community gem host". https://rubygems.org/. 
  2. "Global fake configuration - Fakes - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/fakes/global-fake-configuration. 
  3. "What's a Ruby DSL and what isn't?". https://www.infoq.com/news/2007/06/dsl-or-not. 
  4. "Contract Tests - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/contract-tests. 
  5. "What are idempotent and/or safe methods? - The RESTful cookbook". http://restcookbook.com/HTTP%20Methods/idempotency/. 
  6. "Safe Stubbing - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/safe-stubbing. 
  7. "Safe mocking - Safe Stubbing - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/safe-stubbing/safe-mocking. 
  8. "Bogus: A Brief Introduction" (in en). http://bogus-wrug-2013-07.herokuapp.com. 
  9. "Argument matchers - Safe Stubbing - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/safe-stubbing/argument-matchers. 
  10. "Configuration Options - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/configuration. 
  11. "search_modules - Configuration Options - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/configuration/search-modules. 
  12. "Class: ActiveRecord::Base — Documentation for rails (3.1.1)". http://www.rubydoc.info/docs/rails/3.1.1/ActiveRecord/Base. 
  13. "fake_ar_attributes - Configuration Options - Bogus - bogus - Relish". https://www.relishapp.com/bogus/bogus/v/0-1-6/docs/configuration/fake-ar-attributes. 

Sources

External links