Question

I want to mock a class with Ruby.

How do I write a method that will take care of the boilerplate code?

The following code:

module Mailgun

end

module Acani
  def self.mock_mailgun(mock)
    temp = Mailgun
    const_set(:Mailgun, mock)
    p Mailgun
    yield
  ensure
    const_set(:Mailgun, temp)
  end
end

Acani.mock_mailgun('mock') { p Mailgun }

prints:

"mock"
Mailgun

What's going on here? Why is Mailgun its original value inside the block? Does this have to do with Ruby bindings?

Ruby version: 2.1.1p76

Was it helpful?

Solution

Try putting Object. before each const_set.

The code in the question is simplified. Here is the pertinent code:

test/test_helper.rb

require 'minitest/autorun'

module Acani
  def self.const_mock(const, mock)
    temp = const_get(const)
    const_set_silent(const, mock)
    yield
  ensure
    const_set_silent(const, temp)
  end

  private

  def self.const_set_silent(const, value)
    temp = $VERBOSE
    $VERBOSE = nil
    Object.const_set(const, value)
  ensure
    $VERBOSE = temp
  end
end

test/web_test.rb

require 'test_helper'
require 'rack/test'
require_relative '../web'

class AppTest < MiniTest::Test
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  def test_password_reset
    post '/users', {email: 'user1@gmail.com', password: 'password1'}

    mailgun_mock = MiniTest::Mock.new
    mailgun_mock.expect(:send, 200, [Hash])

    Acani.const_mock(:Mailgun, mailgun_mock) do
      post '/password_resets', {email: 'user1@gmail.com'}
    end

    mailgun_mock.verify
    assert_equal 201, last_response.status
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top