# When CouchDB view functions get complex, it's nice to be able to unit test them. # When your development dataset is large, and views take some time to build, better # to find bugs via unit tests over having to wait for a view build to see if things # are working correctly. # This implementation uses Johnson for executing Javascript -- see the evaluate_map method # at the end of this file. Note that we can define the emit and log functions as Ruby Proc # objects and have them work in the Javascript context. Pretty cool. # I think it would be possible to test show and list functions as well, using a similar # technique to mock out the getRow() and send() functions. require 'rubygems' require "johnson" require "json" require "test/unit" # Johnson monkeypatch, so that assert_equal works as expected with array values class Johnson::SpiderMonkey::RubyLandProxy def ==(other) case other when Array to_a == other else super end end end class TestViews < Test::Unit::TestCase def setup # Set the design doc in an instance variable. In practice, you'll want to get the # design doc from the server using your preferred HTTP client, ex: # # @ddoc = JSON.parse RestClient.get('localhost:5984/mydb/_design/myapp') # # If you're getting the same ddoc again and again for each test, you can store it in a # class variable or constant on test suite startup, thus avoiding the overhead of # HTTP and JSON parsing of the ddoc for each test. @ddoc = JSON.parse(<<-EOV) { "_id": "_design/myapp", "views": { "by_city": { "map": "function(doc) { emit([doc.city, doc.name], doc); }" } } } EOV end def test_by_city mapfun = @ddoc['views']['by_city']['map'] doc = {"name" => "Geoff", "city" => "Chicago", "state" => "IL"} rows = evaluate_map(mapfun, doc)['rows'] assert_equal 1, rows.size assert_equal ["Chicago", "Geoff"], rows.first['key'] assert_equal doc, rows.first['value'] end private def evaluate_map(fun, doc) rows, log = [], [] emitfun = Proc.new {|k, v| rows << {"key" => k, "value" => v}} logfun = Proc.new {|m| log << m} map = Johnson.evaluate(fun, :emit => emitfun, :log => logfun) map.call(doc) {"rows" => rows, "log" => log} end end