Module: FunctionsFramework::Testing

Extended by:
Testing
Included in:
Testing
Defined in:
lib/functions_framework/testing.rb

Overview

Helpers for writing unit tests.

Methods on this module can be called as module methods, or this module can be included in a test class.

Example

Suppose we have the following app that uses the functions framework:

# app.rb

require "functions_framework"

FunctionsFramework.http "my-function" do |request|
  "Hello, world!"
end

The following is a test that could be run against that app:

# test_app.rb

require "minitest/autorun"
require "functions_framework/testing"

class MyTest < Minitest::Test
  # Make the testing methods available.
  include FunctionsFramework::Testing

  def test_my_function
    # Load app.rb and apply its functions within this block
    load_temporary "app.rb" do
      # Create a mock http (rack) request
      request = make_get_request "http://example.com"

      # Call the function and get a rack response
      response = call_http "my-function", request

      # Assert against the response
      assert_equal "Hello, world!", response.body.join
    end
  end
end

Instance Method Summary collapse

Instance Method Details

#call_event(name, event, globals: nil, logger: nil) ⇒ nil

Call the given event function for testing. The underlying function must be of type :cloud_event`.

By default, the startup tasks will be run for the given function if they have not already been run. You can, however, disable running startup tasks by providing an explicit globals hash.

By default, the FunctionsFramework.logger will be used, but you can override that by providing your own logger. In particular, to disable logging, you can pass Logger.new(nil).

Parameters:

  • name (String)

    The name of the function to call

  • event (::CloudEvents::Event)

    The event to send

  • globals (Hash) (defaults to: nil)

    Do not run startup tasks, and instead provide the globals directly. Optional.

  • logger (Logger) (defaults to: nil)

    Use the given logger instead of the Functions Framework's global logger. Optional.

Returns:

  • (nil)


170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/functions_framework/testing.rb', line 170

def call_event name, event, globals: nil, logger: nil
  globals ||= run_startup_tasks name, logger: logger, lenient: true
  function = Testing.current_registry[name]
  case function&.type
  when :cloud_event
    function.call event, globals: globals, logger: logger
    nil
  when nil
    raise "Unknown function name #{name}"
  else
    raise "Function #{name} is not a CloudEvent function"
  end
end

#call_http(name, request, globals: nil, logger: nil) ⇒ Rack::Response

Call the given HTTP function for testing. The underlying function must be of type :http. Returns the Rack response.

By default, the startup tasks will be run for the given function if they have not already been run. You can, however, disable running startup tasks by providing an explicit globals hash.

By default, the FunctionsFramework.logger will be used, but you can override that by providing your own logger. In particular, to disable logging, you can pass Logger.new(nil).

Parameters:

  • name (String)

    The name of the function to call

  • request (Rack::Request)

    The Rack request to send

  • globals (Hash) (defaults to: nil)

    Do not run startup tasks, and instead provide the globals directly. Optional.

  • logger (Logger) (defaults to: nil)

    Use the given logger instead of the Functions Framework's global logger. Optional.

Returns:

  • (Rack::Response)


135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/functions_framework/testing.rb', line 135

def call_http name, request, globals: nil, logger: nil
  globals ||= run_startup_tasks name, logger: logger, lenient: true
  function = Testing.current_registry[name]
  case function&.type
  when :http
    Testing.interpret_response do
      function.call request, globals: globals, logger: logger
    end
  when nil
    raise "Unknown function name #{name}"
  else
    raise "Function #{name} is not an HTTP function"
  end
end

#load_temporary(path, &block) ⇒ Object

Load the given functions source for the duration of the given block, and restore the previous status afterward.

Parameters:

  • path (String)

    File path to load



73
74
75
76
# File 'lib/functions_framework/testing.rb', line 73

def load_temporary path, &block
  path = ::File.expand_path path
  Testing.load_for_testing path, &block
end

#make_cloud_event(data, id: nil, source: nil, type: nil, spec_version: nil, data_content_type: nil, data_schema: nil, subject: nil, time: nil) ⇒ ::CloudEvents::Event

Make a simple CloudEvent, for passing to a function test. The event data is required, but all other parameters are optional (i.e. a reasonable or random value will be generated if not provided).

Parameters:

  • data (Object)

    The data

  • id (String) (defaults to: nil)

    Event ID (optional)

  • source (String, URI) (defaults to: nil)

    Event source (optional)

  • type (String) (defaults to: nil)

    Event type (optional)

  • spec_version (String) (defaults to: nil)

    Spec version (optional)

  • data_content_type (String, ::CloudEvents::ContentType) (defaults to: nil)

    Content type for the data (optional)

  • data_schema (String, URI) (defaults to: nil)

    Data schema (optional)

  • subject (String) (defaults to: nil)

    Subject (optional)

  • time (String, DateTime) (defaults to: nil)

    Event timestamp (optional)

Returns:

  • (::CloudEvents::Event)


249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/functions_framework/testing.rb', line 249

def make_cloud_event data,
                     id: nil,
                     source: nil,
                     type: nil,
                     spec_version: nil,
                     data_content_type: nil,
                     data_schema: nil,
                     subject: nil,
                     time: nil
  id ||= "random-id-#{rand 100_000_000}"
  source ||= "functions-framework-testing"
  type ||= "com.example.test"
  spec_version ||= "1.0"
  ::CloudEvents::Event.new id:                id,
                           source:            source,
                           type:              type,
                           spec_version:      spec_version,
                           data_content_type: data_content_type,
                           data_schema:       data_schema,
                           subject:           subject,
                           time:              time,
                           data:              data
end

#make_get_request(url, headers = []) ⇒ Rack::Request

Make a simple GET request, for passing to a function test.

Parameters:

  • url (URI, String)

    The URL to get.

  • headers (Array, Hash) (defaults to: [])

    HTTP headers. May be given as a hash (of header names mapped to values), an array of strings (where each string is of the form Header-Name: Header value), or an array of two-element string arrays.

Returns:

  • (Rack::Request)


213
214
215
# File 'lib/functions_framework/testing.rb', line 213

def make_get_request url, headers = []
  make_request url, headers: headers
end

#make_post_request(url, body, headers = []) ⇒ Rack::Request

Make a simple POST request, for passing to a function test.

Parameters:

  • url (URI, String)

    The URL to post to.

  • body (String)

    The body to post.

  • headers (Array, Hash) (defaults to: [])

    HTTP headers. May be given as a hash (of header names mapped to values), an array of strings (where each string is of the form Header-Name: Header value), or an array of two-element string arrays.

Returns:

  • (Rack::Request)


228
229
230
# File 'lib/functions_framework/testing.rb', line 228

def make_post_request url, body, headers = []
  make_request url, method: ::Rack::POST, body: body, headers: headers
end

#make_request(url, method: ::Rack::GET, body: nil, headers: []) ⇒ Rack::Request

Make a Rack request, for passing to a function test.

Parameters:

  • url (URI, String)

    The URL to get, including query params.

  • method (String) (defaults to: ::Rack::GET)

    The HTTP method (defaults to "GET").

  • body (String) (defaults to: nil)

    The HTTP body, if any.

  • headers (Array, Hash) (defaults to: [])

    HTTP headers. May be given as a hash (of header names mapped to values), an array of strings (where each string is of the form Header-Name: Header value), or an array of two-element string arrays.

Returns:

  • (Rack::Request)


196
197
198
199
200
201
# File 'lib/functions_framework/testing.rb', line 196

def make_request url, method: ::Rack::GET, body: nil, headers: []
  env = Testing.build_standard_env URI(url), headers
  env[::Rack::REQUEST_METHOD] = method
  env[::Rack::RACK_INPUT] = ::StringIO.new body if body
  ::Rack::Request.new env
end

#run_startup_tasks(name, logger: nil, lenient: false) ⇒ Hash

Run startup tasks for the given function name and return the initialized globals hash.

Normally, this will be run automatically prior to the first call to the function using #call_http or #call_event, if it has not already been run. However, you can call it explicitly to test its behavior. It cannot be called more than once for any given function.

By default, the FunctionsFramework.logger will be used, but you can override that by providing your own logger. In particular, to disable logging, you can pass Logger.new(nil).

Parameters:

  • name (String)

    The name of the function to start up.

  • logger (Logger) (defaults to: nil)

    Use the given logger instead of the Functions Framework's global logger. Optional.

  • lenient (Boolean) (defaults to: false)

    If false (the default), raise an error if the given function has already had its startup tasks run. If true, duplicate requests to run startup tasks are ignored.

Returns:

  • (Hash)

    The initialized globals.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/functions_framework/testing.rb', line 99

def run_startup_tasks name, logger: nil, lenient: false
  function = Testing.current_registry[name]
  raise "Unknown function name #{name}" unless function
  globals = Testing.current_globals name
  if globals
    raise "Function #{name} has already been started up" unless lenient
  else
    globals = function.populate_globals
    Testing.current_registry.startup_tasks.each do |task|
      task.call function, globals: globals, logger: logger
    end
    Testing.current_globals name, globals
  end
  globals.freeze
end