2
votes

Simplecov detected that I was missing some tests on my lib/api_verson.rb class:

class ApiVersion

  def initialize(version)
    @version = version
  end

  def matches?(request)
    versioned_accept_header?(request) || version_one?(request)
  end

  private

  def versioned_accept_header?(request)
    accept = request.headers['Accept']
    accept && accept[/application\/vnd\.#{Rails.application.secrets.my_app_accept_header}-v#{@version}\+json/]
  end

  def unversioned_accept_header?(request)
    accept = request.headers['Accept']
    accept.blank? || accept[/application\/vnd\.#{Rails.application.secrets.my_app_accept_header}/].nil?
  end

  def version_one?(request)
    @version == Rails.application.secrets.my_app_default_api_version && unversioned_accept_header?(request)
  end

end

This class is used by the routes file to help setup api versions:

namespace :api, path: "", defaults: {format: :json} do

scope module: :v1, constraints: ApiVersion.new(1) do
  get '/alive', to: 'api#alive'
end

scope module: :v2, constraints: ApiVersion.new(2) do
  get '/alive', to: 'api#alive'
end

end

This setup was ported from versioning_your_ap_is.

I am trying to test the methods here that simplecov is reporting as failures, and right now I am stuck on the case where the private method contains a private method...

  def version_one?(request)
    @version == Rails.application.secrets.my_app_default_api_version && unversioned_accept_header?(request)
  end

This is my current spec:

require 'spec_helper'

describe ApiVersion do

  before(:each) do
    @apiversion = ApiVersion.new(1)
    @current_api_version = Rails.application.secrets.my_app_default_api_version
    @request = ActionController::TestRequest.new(host: 'localhost')
    @request.headers["Accept"] = "application/vnd.#{Rails.application.secrets.my_app_accept_header}-v#{@current_api_version}+json"
  end

  describe "Method #versioned_accept_header? =>" do
    it "Should return false if the header accept variable contains application/vnd.#{Rails.application.secrets.my_app_accept_header}-v#{@current_api_version}+json" do
      expect(@apiversion.send(:unversioned_accept_header?, @request)).to eq(false)
    end
    it "Should return true if no version is included with the header" do
      request = @request
      request.headers["Accept"] = nil
      expect(@apiversion.send(:unversioned_accept_header?, request)).to eq(true)
    end
  end

  describe "Method #version_one? =>" do
    it "test me" do
      # @apiversion.send(:unversioned_accept_header?, @request)

      binding.pry

      # expect(@apiversion.send(:version_one?, @request)).to eq(false)
    end
  end

end

How do I stub the nested private method to test the version_one private method?

1
you could also use api-versions gem and not write that tests by yourself ;)twonegatives
I thought about that gem actually, but opted to go with this approach instead. I may regret that... It's getting late here on PST time, I may check that out tomorrow.Chris Hough

1 Answers

2
votes

Here is how I ended up with my final tests and coverage at 100%.

The lib/api_version.rb file:

class ApiVersion

  def initialize(version)
    @version = version
  end

  def matches?(request)
    versioned_accept_header?(request) || version_default?(request)
  end

  private

  def versioned_accept_header?(request)
    accept = request.headers['Accept']
    accept && accept[/application\/vnd\.#{Rails.application.secrets.my_app_accept_header}-v#{@version}\+json/]
  end

  def unversioned_accept_header?(request)
    accept = request.headers['Accept']
    accept.blank? || accept[/application\/vnd\.#{Rails.application.secrets.my_app_accept_header}/].nil?
  end

  def version_default?(request)
    @version == Rails.application.secrets.my_app_default_api_version && unversioned_accept_header?(request)
  end

end

Followed up by all of the rspec tests:

require 'spec_helper'

describe ApiVersion do

  before(:each) do
    @apiversion = ApiVersion.new(1)
    @current_api_version = Rails.application.secrets.my_app_default_api_version
    @request = ActionController::TestRequest.new(host: 'localhost')
  end

  describe "Method #matches? =>" do
    it "Should return false when the header is not versioned and it's not the header default." do
      @apiversion.stub(:versioned_accept_header?).and_return(false)
      @apiversion.stub(:version_default?).and_return(false)
      expect(@apiversion.matches?(@request)).to eq(false)
    end
    it "Should return true when the proper header has been supplied but is unversioned." do
      @apiversion.stub(:versioned_accept_header?).and_return(true)
      @apiversion.stub(:version_default?).and_return(false)
      expect(@apiversion.matches?(@request)).to eq(true)
    end
    it "Should return true when the proper header has been supplied and is versioned." do
      @apiversion.stub(:versioned_accept_header?).and_return(true)
      @apiversion.stub(:version_default?).and_return(true)
      expect(@apiversion.matches?(@request)).to eq(true)
    end
  end

  describe "Private method #unversioned_accept_header? =>" do
    it "Should return false if the header accept variable contains version 'application/vnd.#{Rails.application.secrets.my_app_accept_header}' in it." do
      @request.headers["Accept"] = "application/vnd.#{Rails.application.secrets.my_app_accept_header}"
      expect(@apiversion.send(:unversioned_accept_header?, @request)).to eq(false)
    end
    it "Should return true if no version is included with the header." do
      @request.headers["Accept"] = nil
      expect(@apiversion.send(:unversioned_accept_header?, @request)).to eq(true)
    end
  end

  describe "Private method #versioned_accept_header? =>" do
    it "Should return true if the header accept variable contains version 'application/vnd.#{Rails.application.secrets.my_app_accept_header}.v#{Rails.application.secrets.my_app_default_api_version}+json' in it." do
      @header = "application/vnd.#{Rails.application.secrets.my_app_accept_header}-v#{Rails.application.secrets.my_app_default_api_version}+json"
      @request.headers["Accept"] = @header
      expect(@apiversion.send(:versioned_accept_header?, @request)).to eq(@header)
    end
  end

  describe "Private method #version_default? =>" do
    it "Should return false when the proper header version is supplied." do
      @apiversion.stub(:unversioned_accept_header?).and_return(false)
      expect(@apiversion.send(:version_default?, @request)).to eq(false)
    end
    it "Should return true when no header is supplied, or a header different than 'application/vnd.#{Rails.application.secrets.my_app_accept_header}' is supplied." do
      @apiversion.stub(:unversioned_accept_header?).and_return(true)
      expect(@apiversion.send(:version_default?, @request)).to eq(true)
    end
  end

end