Handy gems for minitest-rails

Welcome to ‘minitest’ world! Minitest is yet another ruby testing framework but it’s not a replacement to rspec. In rspec you can readily use things like ‘subject’, ‘metadata’ etc. However, minitest, as the name suggests, is a light-weight testing framework and does not include a lot of these features automatically. Here is a list of gems that you can use to enhance minitest-rails.

1. m

m is a test runner similar to rspec command in rspec framework. Using this we can run single test using line numbers too. Users who don’t like this gem can also run tests using default rake tasks provided by minitest.

2. minitest-metadata

metadata is one of the cool features in rspec. Using this we can tag scenarios across files. Unfortunately minitest doesn’t support this feature by default. We have to include a separate gem called minitest-metadata. We can implement rspec conditional hooks functionality through this gem as follows:

  require "test_helper"

  describe UsersController do

    before do
      @user = create(:user) if metadata[:before_each] == true

    it "must create user" do
      assert_difference('User.count') do
        post :create, user: attributes_for(:user)

      assert_redirected_to user_path(assigns(:user))

    it "must show user", before_each: true do
      get :show, id: @user
      assert_response :success

When we run the file before code block will run only for the latter test case.

3. minitest-implicit-subject

subject is one of the best ways to dry up your specs. To dry up your specs in minitest you have explicity include a gem called minitest-implicit-subject. Here is an example:

  require "test_helper"

  describe User do
    it "foo returns bar" do
      subject.must_equal User

There is slight difference that I have observed in minitest subject from rspec subject. In minitest if you write subject it returns current class name under which it has included. In this scenario it returns output as User class not User object. But in rspec it returns User object.

4. minitest-spec-expect

rspec expectation framework lets you code example more readable. In minitest there is separate gem called minitest-spec-expect available to make your code example readable as same as in rspec. Here is an example:

  require "test_helper"

  describe User do
    it "must be valid" do
      expect(subject.new.valid?).to_equal true

5. minitest-rails-capybara

Using capybara is one of the ways to implement your integration test suite in rspec. We need to include separate gem called minitest-rails-capybara in minitest to write integration tests using the capybara DSL. Here is a simple example:

   require "test_helper"

   feature "User" do
     scenario "should be able save" do
       visit users_path
       click_link 'New User'
       fill_in "user_first_name", with: 'Siva'
       click_button 'Create User'

       expect(User.all.count).to_equal 1

In my next post I will talk about the gems that allow us to mock and stub requests in minitest.

Pro tips for writing better rspec tests

Writing ‘good code’ is always a challenge for every project. Unfortunately, we always associate it with development and not tests. What about automation test code? Ever noticed that you have to write more automation code than the actual lines of development code?

Improper automation code leads to slow running specs, huge LOC in your spec files and a whole lot of confusion when you open a spec file. So, how does one avoid these problems?

Here are a few guidelines that I learned from my experience:

Tip #1: Follow Rspec style guide

That was obvious, wasn’t it? As surprising as it may seem, we invariably do not follow in detail.

  • How often do you see more than one expectation in an example? bad
  • How often do you see lots of stubs and mocks in test code? bad – keep it as simple as you can.
  • How often do you see the use of context, especially in controllers? good

A lot of them are mentioned in the Style guide and we shall repeat below a few which require more details.

Tip #2: Independent tests

We could use the word idempotent here but as a thumb rule there should not be any dependencies across specs and if we run the test multiple times in succession, it should give the same result So, it’s very important that every spec  run in clean database environment. Use database cleaner gem properly, so that you don’t end of having to seed the database before each test! So, if you don’t want roles and countries being deleted,

config.before(:suite) do
  DatabaseCleaner.strategy = :truncation, {:except => %w[roles countries]}

Tip #3: Use expect and not should syntax

It’s just nice and clean!  Using the Transpec gem helps you to convert should syntax to expect syntax

expect(User.all.count).to eq(1)

instead of

User.all.count.should == 1

Tip#4: Use the new syntax for object creation

Use the new FactoryGirl syntax that is supported in factory_girl4. This helps the code to be clean of any harsh gem dependent syntax of class names!


instead of


Tip #5: Use let variables instead of instance variables

This makes code look cleaner. For better understanding about let read the answer given by Myron. Here is an example

describe Project do
  let(:project) { build(:project) }

  it "project should be valid" do
    expect(project.valid?).to eq(true)

Now, when the example executes it defines a method called project if its not defined already or simply returns the value. Fast and efficient.

Tip #6: Use correct expectations for a spec

Obvious as it seems – the test we write should test the write stuff – not just make the test pass! Here is an example of making a test case pass.

context "#edit" do
  it "user should be able to edit his profile" do
    get :edit, id: user.id
    expect(response).to be_success

So, your expectation should ideally be

describe UsersController do
  context "#edit" do
    it "user should be able to edit his profile" do
      get :edit, id: user.id
      expect(response).to render_template("edit")

Here #edit is a method defined in UsersController. So I defined a context in my specs.

Do remember that by default all controller specs stub the template rendering unless and until you include render_views

Tip #7: Use shared examples.

When you want to test the same functionality across different subjects, for example different kinds of users, use shared examples.

context "#edit" do
  it "admin should be able to edit his profile"
  it "distributor should be able to edit his profile"

You can write as

context "#edit" do
  shared_examples "profile_editing" do
    it "user should be able to edit his profile"

  context "admin" do
    include_examples "profile_editing"
    it "should be able see to list of distributors"

  context "distributor" do
    include_examples "profile_editing"
    it "should not able to see list of distributors"

A word of caution – you may loose readability if you use too many shared examples in single file.

Tip #8: Use conditional hooks

Sometimes if you want to execute some piece of code only for one example but not for all examples in a context then you can use conditional hooks.

context "#update" do
  before(:each, run: true) do
    # do something

  it "example1", run: :true
  it "example2"

Now before(:each) will run only for first example but not for second example.

Tip #9: Profiling your examples

rspec gives you an option to examine how much time does a example group take to run.  Simply run specs with the –profile flag.

rspec ./spec/models/user_spec.rb:51 --profile
# output:
0.22431 seconds ./spec/models/user_spec.rb:51

Tip #10: Tagging

When you have a large test suite and you changed some code, how do you ensure that your change didn’t breaks existing functionality? One way is to run your specs locally then push then your Continuous Integration server that runs all specs. This is time consuming if you want to test only a specific set of tests.

Tag them! Run your module specified specs by using tags. For example, suppose we have a Commission feature and we have written model specs, controller specs and integration specs for it. We can easily tag each file with commission:true.  So when we change something related to commissions,  we can run only commission related specs!. We can tag our scenarios as below

   # spec/models/commission_spec.rb
   describe Commission, commission: true do
     # do something
rspec --tag commission

Note: We can tag entire context or a single example in the spec file. For more information please follow this link

All these tips are a results of practical problems I have faced and overcome and a result of finding out the best we can do to improve our own testing. More suggestions and tips are welcome!

Paypal payflow setup in rails

After long time of automation testing on rspec, recently I have started doing development. The initial task itself an interesting task – Paypal Payflow setup as a payment gateway for my rails application. Even though it took me three days to complete it :),  I finally I did it and this gave me feeling that I learnt something new.

Let’s see how we can setup Payflow payment gateway for a rails application. It is a four step process:

  1. Setup test payflow account with paypal.
  2. Add Paypal API credentials in your application.
  3. Fetch secure token before making payment.
  4. Redirect to paypal hosted pages for payment and handling the callback.

Setup test payflow account with Paypal

This is pretty straight forward and here is a good article which explains with screen shots how can we setup for test payflow merchant account.

Add Paypal API credentials in your application

The login credentials can be used as API credentials to call Paypal API. Save the credentials on config/initializers/paypal.rb.

# config/initializers/paypal.rb

unless Rails.env.production?
  PAYPAL_CONFIG = { :mode => 'test',
                    :partner => 'Paypal',
                    :currency => 'USD',
                    :payflow_url => 'https://pilot-payflowpro.paypal.com',
                    :gateway => "https://payflowlink.paypal.com"    }
else  # only production
  PAYPAL_CONFIG = { :partner => 'Paypal',
                    :currency => 'USD',
                    :payflow_url => 'https://payflowpro.paypal.com',
                    :gateway => "https://payflowlink.paypal.com"}

Fetch secure token before making payment

This is the most important step. Using a secure token we can make secure transaction with paypal server. This token is valid only for five minutes. After five minutes you can’t complete the transaction.  In my application I have used ‘curb’ (a curl ruby wrapper) to generate HTTP post request. You can alternatively use http/net or HttParty or Faraday.

module Paypal
  class Configuration
    def self.gen_secure_token(amount, order)
       curl = Curl::Easy.new
       curl.ssl_version = Curl::CURL_SSLVERSION_SSLv3
       curl.url = PAYPAL_CONFIG[:payflow_url]
       success = curl.http_post(Curl::PostField.content('TRXTYPE', 'A'),
                         Curl::PostField.content('TENDER', 'C'),
                         Curl::PostField.content('AMT', amount.round),
                         Curl::PostField.content('ORDERID', order.id),
                         Curl::PostField.content('CURRENCY', PAYPAL_CONFIG[:currency]),
                         Curl::PostField.content('CREATESECURETOKEN', 'Y'),
                         Curl::PostField.content('SECURETOKENID', SecureRandom.hex(18)),
                         Curl::PostField.content('PARTNER', PAYPAL_CONFIG[:partner]),
                         Curl::PostField.content('VENDOR', <paypal_login>),
                         Curl::PostField.content('USER', <paypal_login>),
                         Curl::PostField.content('PWD', <paypal_password>)
       if success
          recd_string = curl.body_str.split('&').collect{|i| i.split('=')}

I am generating a secure token for an order and for a particular amount. Below is the explanation for the parameters:

TNXTYPE: Transaction Type. ‘A’ – Authorization request will be used to generate secure token.(mandatory)
TENDER : Method of payment. ‘C’ stands for it’s credit card payment.(mandatory)
AMT : Transaction Amount(mandatory)
ORDERID: For which order you are doing transaction.
CURRENCY: Currency of the transaction(here it is USD)
CREATESECURETOKEN: Boolean value whether paypal should needs to create secure token or not.
SECURETOKENID: Before generating secure token we need to generate secure token ID.  Both secure token ID and secure token will make a unique transaction. It should be ’18’ characters.
PARTNER: it is always ‘paypal’
VENDOR : Paypal login
USER: paypal login
PWD: Paypal password.

Note: Before sending the request to paypal you have to set SSL version. Otherwise paypal won’t give any secure token. This request gets sent to https://pilot-payflowpro.paypal.com on testing mode and to https://payflowpro.paypal.com in production mode.

Redirect to paypal hosted pages for payment and handling the callback

After we get secure token then we have to submit the form with secure ID and secure token to https://payflowlink.paypal.com.

<form accept-charset="UTF-8" action="https://payflowlink.paypal.com" id="pay" method="post">
  <input id="SECURETOKEN" name="SECURETOKEN" type="hidden" value="1j3G4l8NGMUyUOulrB3I0bAAQ">
  <input id="SECURETOKENID" name="SECURETOKENID" type="hidden" value="801f9cfb2dd4127002138e21794f0e77b252">
  <input id="mode" name="mode" type="hidden" value="test"> <!-- Comment this line when your are running app on production mode -->

As we are using paypal hosted pages for doing payment, it redirects to paypal website where the can actually do payment. You can customize hosted pages by logging on to your paypal account. You can even set the success and failure redirect URLs in the hosted pages configuration.

This process ensures that there is never any sensitive credit card or payment information being sent to our server, hence we have auto-compliance for the website and don’t require any PCI compliance or even an SSL certificate!

I hope after reading this article no one will spend that much time that I have spent on this to integrate. Suggestions would be welcome.

RSpec Custom Matchers

Everyone knows what are RSpec matchers when you are writing RSpec code for your project. If you don’t know, it’s better to read before read this article. Just google it ‘as rspec matchers’ you will be find many links and follow any one link which you like.

By default, RSpec provide us many matchers. At the same time RSpec provide us a way to create our custom matchers depends upon our requirements. Now let see how to write our own custom matchers.

In this article I have written matchers for presence, uniqueness and format validations.

Now place following code on ‘spec/support/custom/matchers.rb’

# spec/support/custom/matchers.rb
# Following matchers are useful if you are using mongodb with mongoid
module Custom
  module Matcher
   RSpec::Matchers.define :be_present do |attribute|
     match do |target|
       attributes = target.class.validators.collect{|a| a.attributes if a.class ==                                     ActiveModel::Validations::PresenceValidator}.compact.flatten

     failure_message_for_should do |target|
       "expecting true but got false"

     failure_message_for_should_not do |target|
       "expecting false but got true"

   RSpec::Matchers.define :be_unique do |attribute|
      match do |target|
        attributes = target.class.validators.collect{|a| a.attributes if a.class ==  Mongoid::Validations::UniquenessValidator}.compact.flatten

      failure_message_for_should do |target|
        "expecting true but got false"

      failure_message_for_should_not do |target|
         "expecting false but got true"

    RSpec::Matchers.define :be_in_valid_format do |attribute|
       match do |target|
          attributes = target.class.validators.collect{|a| a.attributes if a.class == ActiveModel::Validations::FormatValidator}.compact.flatten

       failure_message_for_should do |target|
         "expecting true but got false"

       failure_message_for_should_not do |target|
          "expecting false but got true"

Now you if you want test uniqueness of an attribute then just write in your spec file as


   subject { User.new }
   it { should be_unique(:email) }


When your are executing user specs ‘be_unique’ will call our predefine matcher on object which we set in subject block. In our case the subject is user object. After that our matcher code is executed and then return either true or false as a result. Depends on return value and expectation we have used our test case will pass or fail.

Not only we can define our own matchers but we can override the failure messages by over-riding ‘failure_message_for_should’ and ‘failure_message_for_should_not’ methods. Whenever any failure encountered these methods will invoked automatically.

I hope you have learned something new today. Any feedback or suggestions would be welcome.

Upstart Scripts in Ubuntu

Upstart is an event based tool that handles starting of services during system booting and shut-down the services before system is shut down. It also monitors the services while they running. It was designed to overcome the limitations in system V and dependency based init systems.

Limitations in existing systems

  1. They are not dynamic in nature. That means when we plug in a new devise like a monitor after the system has booted up, the system cannot recognize it without user intervention.
  2. There is no parallelism. This means when we start certain services, like mysql which is dependent on some other services, they should start before mysql service has started. To start these dependent services, it requires manual configuration in /etc/rc.X directories and is prone to errors.
  3. In some systems, a brute force approach is used to ensure all dependent services has started. This bring load on the system and is sometimes in-efficient.

To get more limitations on existing systems, you can read more information here. Upstart overcomes these limitations by starting services only when all conditions are met. And there would be no kernel panic if init fails as it is designed in a very clean and sensible way.

Triggering Jobs

Upstart scripts are located in /etc/init/ directory with a .conf extension. The scripts are called ‘System Jobs’ and run using sudo privileges. Just like system jobs we also have ‘User Jobs’ that are located at $HOME/.init/ directory. (Note: Only Ubuntu versions above 11.10 support user jobs). After internal upstart initialization, it emits an event called ‘startup’ which triggers rest of system and user jobs.

Writing Upstart scripts

An Upstart script is a combination of states and events. On particular events the service state changes from one to another. Currently upstart supports 10 states. They are waiting, starting, pre-start, spawned, post-start, running, pre-stop, stopping, killed and post-stop. More details here. Here is a simple upstart script which starts node.js server whenever system boots.

# /etc/init/nodejs.conf

description "node.js server"
author      "Siva Gollapalli"

# used to be: start on startup
# until we found some mounts weren't ready yet while booting:
#start on started mountall
# If network interface is wireless
start on (local-filesystems and net-device-up IFACE=wlan0)
# If network interface is Ethernet uncomment below line and comment above line
#start on (local-filesystems and net-device-up IFACE=eth0)

stop on shutdown

# Automatically Respawn:
respawn limit 99 5

    # Not sure why $HOME is needed, but we found that it is:
    export HOME="/home/siva/work/myproject"

    exec /usr/local/bin/node $HOME/node/notify.js 13002 >> /var/log/node.log 2>&1
end script

post-start script
   # Optionally put a script here that will notify you node has (re)started
   # /root/bin/hoptoad.sh "node.js has started!"
end script

Stanzas and Configuration

‘description’, ‘author’, ‘start on’, ‘stop on’, ‘respawn’, ‘script’ and ‘post-start’ are called as stanzas.

start on (local-filesystems and net-device-up IFACE=wlan0)

This tells Upstart to start node.js service when all local-filesystems and wireless network interface is up.

start on (local-filesystems and net-device-up IFACE=eth0)

This tells Upstart to start node.js service when all local-filesystems are up and Ethernet network interface is up.

Before starting node.js service Upstart executes the pre-start block if it exists. Later it executes the script block which is the actual code used to start a service. After service has been started, upstart executes post-start block.

In our current scenario with node.js service, there are no pre-start block and the post-start block is there with no code. So, upstart just start node.js service without any pre and post actions.

Monitoring services

You may not have noticed that I forgot to explain what is ‘respawn’ and when to use 🙂 ‘respawn’ is a stanza which executes when the service has exited for some reason and you want start the service again automatically then this will be used.

‘respawn limit’ defines the limit of re-tries for a service. In our current scenario it is 99 times after a time interval of 5 seconds. That means, try 99 times every 5 seconds to get our service started.

Note: It is preferred not to ‘respawn’ unless and until you are sure that the code you have written is correct. Otherwise it could leads to problems on CPU utilization and memory constraints.

After system has been started execute following command to know status of the service

sudo initctl nodejs status

Stopping services

Up till now we have seen how to start a service. When system is about shut down Upstart executes pre-stop block similar to pre-start block and then executes post-stop block after service has been shut down which is similar to post-start. In out current scenario node.js service stops when system is shut down. We do not need to do anything special to stop services on shutdown. Its guaranteed to cleanly shutdown processes in the correct order!

I hope everyone enjoys this article and let me know if you face any complex scenarios to write. I will give a shot. Feedback and suggestions are welcome.

Payment gateway testing using webmock

Testing the payment gateway involves lots of scenarios like what should happen if exception is raised while doing payment. To test this scenario we have to call payment gateway API and take response. But herein lies a problem. The test code you have written may work sometime and won’t work another time. The reason behind this failure is, whenever you call payment gateway in quick succession, then gateway gives error. So, we can’t test payment scenarios by calling actual gateway API. How can we then test gateway integration? The answer is stubbing the gateway requests using webmock.

Testing recurring payment with authorize.net

  1. For configure ‘webmock’ for test environment and do bundle install.
  2. Executes your controller specs. Anyway, specs will fail as we disable all real HTTP requests.
  3. Download the recurring payment response from authorize.net using curl like below:
curl -H 'Content-Type:text/xml' <?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ARBCreateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">\n  <merchantAuthentication>\n    <name>2ErYn5tb5X</name>\n    <transactionKey>75mhe6B5TLNwA47b</transactionKey>\n  </merchantAuthentication>\n  <subscription>\n    <paymentSchedule>\n      <interval>\n        <length>1</length>\n        <unit>months</unit>\n      </interval>\n      <startDate>2011-09-14</startDate>\n      <totalOccurrences>1</totalOccurrences>\n    </paymentSchedule>\n    <amount>10.00</amount>\n    <payment>\n      <creditCard>\n        <cardNumber>370000000000002</cardNumber>\n        <expirationDate>2011-21</expirationDate>\n      </creditCard>\n    </payment>\n    <billTo>\n      <firstName>Test</firstName>\n      <lastName>Account</lastName>\n      <company></company>\n      <address></address>\n      <city></city>\n      <state></state>\n      <zip></zip>\n      <country></country>\n    </billTo>\n  </subscription>\n</ARBCreateSubscriptionRequest>\n" https://apitest.authorize.net/xml/v1/request.api > success_response

This sends an XMLHttpPost request to https://apitest.authorize.net/xml/v1/request.api and copies the response to success_reponse file.

We now have response in hand. Place this response in spec/support/webmock/authorize.net directory. Now, we need to configure the response such a way that whenever we send recurring payment request it should return our downloaded response.

Create a file called payment.rb in spec/support directory.

    require 'webmock/rspec'

    RSpec.configure do |config|
     VALID_CARD  = 370000000000002
     FIRST_NAME   = "test"
     LAST_NAME    = "account"
     EXPIRY_YEAR  = (Time.now.year + 10).to_s
     EXPIRY_MONTH = (Time.now.month + 1).to_s
     CVV = "123"

     config.fixture_path = "#{::Rails.root}/spec/fixtures"
     # configure test response for purchase and recurring payment
     authorize_net = config.fixture_path + '/webmock/authorize.net/'
     payment_responses = []
     Dir["#{authorize_net}/**/*"].each do |path|
       payment_responses << path
     payment_gateway = "https://apitest.authorize.net/xml/v1/request.api"
     success_params = []

     # configure recurring payment request parameters with different values
     10.times do |i|
       amount = 5 * i

       # Stubbing request parameters with different values
       success_params << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ARBCreateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">\n  <merchantAuthentication>\n    <name>2ErYn5tb5X</name>\n    <transactionKey>75mhe6B5TLNwA47b</transactionKey>\    n  </merchantAuthentication>\n  <subscription>\n    <paymentSchedule>\n      <interval>\n        <length>1</length>\n        <unit>months</unit>\n      </interval>\n      <startDate>#{(Date.today + 1.month).to_s(:db)}</startDate>\n      <totalOccurrences>1</to    talOccurrences>\n    </paymentSchedule>\n    <amount>#{amount}.00</amount>\n    <payment>\n      <creditCard>\n        <cardNumber>#{VALID_CARD}</cardNumber>\n        <expirationDate>#{EXPIRY_YEAR}-#{EXPIRY_MONTH}</expirationDate>\n      </creditCard>\n    </payment>\n    <billTo>\n      <firstName>#{FIRST_NAME}</firstName>\n      <lastName>#{LAST_NAME}</lastName>\n      <company></company>\n      <address></address>\n      <city></city>\n      <state></state>\n      <zip></zip>\n      <country></country>\n    </billTo>\n  </subscription>\n</ARBCreateSubscriptionRequest>\n"

       config.around(:each) do |example|
         success_params.each { |parameter| WebMock::API.stub_request(:post, payment_gateway).
                with(:body => parameter,
                     :headers => {'Accept'=>'*/*', 'Content-Type'=>'text/xml'}).
                to_return(:status => 200,
                          :body => File.new(payment_responses[0]), :headers => {})}

    WebMock.disable_net_connect!(:allow_localhost => true)

I think you understand how to stub recurring payment requests. Now we will see another example.

Purchase using authorize.net

As usual download purchase response from authorize.net like below:

   curl -H "Content-Type:application/x-www-form-urlencoded" -d login=2ErYn5tb5X -d key=75mhe6B5TLNwA47b -d key=x_type=AUTH_CAPTURE -d x_encap_char=%24 -d x_first_name=test -d x_invoice_num= -d x_last_name=account -d x_tran_key=75mhe6B5TLNwA47b -d x_card_num=370000000000002 -d x_version=3.1 -d x_relay_response=FALSE -d x_exp_date=1021 -d x_login=2ErYn5tb5X -d x_delim_data=TRUE -d x_description= -d x_card_code=123 -d x_amount=30.00 -d x_test_request=FALSE -d x_delim_char=%2C https://test.authorize.net/gateway/transact.dll > purchase

It will send as HTTP Post request to https://test.authorize.net/gateway/transact.dll and copy the response into purchase file. Place this file in spec/fixtures/webmock/authorize.net directory.

Now stub the purchase request:

   stub_request(:post, "https://test.authorize.net/gateway/transact.dll")
        with(:body => /^[A-Za-z&_0-9#%.=]*$/,
             :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded'}).
        to_return(:status => 200, :body => File.new(payment_responses[1]), :headers => {})

Note: We can’t stub request with different parameters as it is a HTTP Post request. So match request parameters against regular expression.

Hope this helps! Please let me know if you see any complex payment scenarios. I will give a shot!.