Back to Repositories

Testing Geocoding Cache Implementation in alexreisner/geocoder

This test suite validates the caching functionality of the Geocoder gem, focusing on cache hits, error handling, and cache management across different geocoding service providers.

Test Coverage Overview

The test suite comprehensively covers cache behavior across multiple geocoding services, with particular attention to cache hits and misses. Key functionality tested includes:

  • Cache hit verification for repeated requests
  • Error condition handling for Google over-query-limit scenarios
  • Bing service unavailability cache behavior
  • Cache expiration functionality

Implementation Analysis

The testing approach employs Ruby’s unit testing framework with a focus on isolating cache behavior. Tests utilize mock responses and service-specific configurations to validate caching mechanisms.

Key patterns include setup/teardown for logger configuration, service-specific test isolation, and cache state verification through instance variables.

Technical Details

Testing tools and configuration:

  • Ruby Test::Unit framework
  • Tempfile for temporary logging
  • Logger configuration for test isolation
  • Service-specific API key management
  • Cache configuration with customizable prefix options

Best Practices Demonstrated

The test suite demonstrates several testing best practices including proper test isolation, thorough error condition coverage, and comprehensive service provider testing.

Notable practices include:
  • Proper resource cleanup in teardown
  • Service-specific error handling verification
  • Explicit cache state verification
  • Modular test organization by functionality

alexreisner/geocoder

test/unit/cache_test.rb

            
# encoding: utf-8
require 'test_helper'

class CacheTest < GeocoderTestCase
  def setup
    @tempfile = Tempfile.new("log")
    @logger = Logger.new(@tempfile.path)
    Geocoder.configure(logger: @logger)
  end

  def teardown
    Geocoder.configure(logger: :kernel)
    @logger.close
    @tempfile.close
  end

  def test_second_occurrence_of_request_is_cache_hit
    Geocoder.configure(:cache => {})
    Geocoder::Lookup.all_services_except_test.each do |l|
      next if
        # local, does not use cache
        l == :maxmind_local ||
        l == :geoip2 ||
        l == :ip2location_lite ||
        # uses the AWS gem, not HTTP requests with caching
        l == :amazon_location_service
      Geocoder.configure(:lookup => l)
      set_api_key!(l)
      results = Geocoder.search("Madison Square Garden")
      assert !results.first.cache_hit,
        "Lookup #{l} returned erroneously cached result."
      results = Geocoder.search("Madison Square Garden")
      assert results.first.cache_hit,
        "Lookup #{l} did not return cached result."
    end
  end

  def test_google_over_query_limit_does_not_hit_cache
    Geocoder.configure(:cache => {})
    Geocoder.configure(:lookup => :google)
    set_api_key!(:google)
    Geocoder.configure(:always_raise => :all)
    assert_raises Geocoder::OverQueryLimitError do
      Geocoder.search("over limit")
    end
    lookup = Geocoder::Lookup.get(:google)
    assert_equal false, lookup.instance_variable_get(:@cache_hit)
    assert_raises Geocoder::OverQueryLimitError do
      Geocoder.search("over limit")
    end
    assert_equal false, lookup.instance_variable_get(:@cache_hit)
  end

  def test_bing_service_unavailable_without_raising_does_not_hit_cache
    Geocoder.configure(cache: {}, lookup: :bing, always_raise: [])
    set_api_key!(:bing)
    lookup = Geocoder::Lookup.get(:bing)

    Geocoder.search("service unavailable")
    assert_false lookup.instance_variable_get(:@cache_hit)

    Geocoder.search("service unavailable")
    assert_false lookup.instance_variable_get(:@cache_hit)
  end

  def test_expire_all_urls
    Geocoder.configure(cache: {}, cache_options: {prefix: "geocoder:"})
    lookup = Geocoder::Lookup.get(:nominatim)
    lookup.cache['http://api.nominatim.com/'] = 'data'
    assert_operator 0, :<, lookup.cache.send(:keys).size
    lookup.cache.expire(:all)
    assert_equal 0, lookup.cache.send(:keys).size
  end
end