philiprehberger-http_mock

Tests Gem Version Last updated

Lightweight HTTP request stubbing for tests

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-http_mock"

Or install directly:

gem install philiprehberger-http_mock

Usage

require "philiprehberger/http_mock"

Philiprehberger::HttpMock.stub(:get, "https://api.example.com/users")
  .to_return(status: 200, body: '{"users":[]}')

response = Philiprehberger::HttpMock.request(:get, "https://api.example.com/users")
response.status  # => 200
response.body    # => '{"users":[]}'

POST with Body Matching

Philiprehberger::HttpMock.stub(:post, "https://api.example.com/users")
  .with(body: '{"name":"Alice"}')
  .to_return(status: 201, body: '{"id":1}')

Header Matching

Philiprehberger::HttpMock.stub(:get, "https://api.example.com/data")
  .with(headers: { "Authorization" => "Bearer token123" })
  .to_return(status: 200, body: "secret")

Response Sequences

Philiprehberger::HttpMock.stub(:get, "https://api.example.com/flaky")
  .to_return_in_sequence([
    { status: 503, body: "unavailable" },
    { status: 200, body: "ok" }
  ])

Philiprehberger::HttpMock.request(:get, "https://api.example.com/flaky").status  # => 503
Philiprehberger::HttpMock.request(:get, "https://api.example.com/flaky").status  # => 200
Philiprehberger::HttpMock.request(:get, "https://api.example.com/flaky").status  # => 200 (repeats last)

Callback Responses

Philiprehberger::HttpMock.stub(:post, "https://api.example.com/echo")
  .to_return { |request| { status: 200, body: request.body.upcase } }

response = Philiprehberger::HttpMock.request(:post, "https://api.example.com/echo", body: "hello")
response.body  # => "HELLO"

Error Simulation

Philiprehberger::HttpMock.stub(:get, "https://api.example.com/fail")
  .to_raise(RuntimeError.new("connection refused"))

Philiprehberger::HttpMock.stub(:post, "https://api.example.com/slow")
  .to_timeout
# raises Philiprehberger::HttpMock::TimeoutError

Method Shortcuts

Philiprehberger::HttpMock.stub_get("https://api.example.com/users")
  .to_return(status: 200, body: '[]')

Philiprehberger::HttpMock.stub_post("https://api.example.com/users")
  .with(body: '{"name":"Alice"}')
  .to_return(status: 201)

Also available: stub_put, stub_patch, stub_delete.

Last Request

Philiprehberger::HttpMock.stub_post("https://api.example.com/users").to_return(status: 201)
Philiprehberger::HttpMock.request(:post, "https://api.example.com/users", body: '{"name":"Bob"}')

Philiprehberger::HttpMock.last_request.body  # => '{"name":"Bob"}'

Stub Verification

stub = Philiprehberger::HttpMock.stub(:get, "https://api.example.com/data")
  .to_return(status: 200)

Philiprehberger::HttpMock.request(:get, "https://api.example.com/data")

stub.called?     # => true
stub.call_count  # => 1

Philiprehberger::HttpMock.verify!  # raises UnmatchedStubError if any stub was never called

Request Recording

Philiprehberger::HttpMock.stub(:get, "https://api.example.com/data").to_return(status: 200)
Philiprehberger::HttpMock.request(:get, "https://api.example.com/data")

requests = Philiprehberger::HttpMock.requests
requests.length      # => 1
requests.first.url   # => "https://api.example.com/data"

Scoped Isolation

Philiprehberger::HttpMock.scope do
  Philiprehberger::HttpMock.stub(:get, "https://api.example.com/temp")
    .to_return(status: 200)

  # Stubs are automatically cleaned up after the block
end

API

HttpMock

Method Description
.stub(method, url) Create a request stub, returns a chainable stub definition
.stub_get(url) Shorthand for .stub(:get, url)
.stub_post(url) Shorthand for .stub(:post, url)
.stub_put(url) Shorthand for .stub(:put, url)
.stub_patch(url) Shorthand for .stub(:patch, url)
.stub_delete(url) Shorthand for .stub(:delete, url)
.request(method, url, body:, headers:) Simulate a request against registered stubs
.requests Get all recorded requests
.last_request Get the most recently recorded request
.verify! Raise UnmatchedStubError if any stub was never called
.reset! Clear all stubs and recorded requests
.scope { ... } Execute a block with isolated stubs

StubDefinition

Method Description
#with(body:, headers:) Add matching constraints for body and/or headers
#to_return(status:, body:, headers:) Set the response to return
`#to_return { \ request\
#to_return_in_sequence(responses) Set an ordered sequence of responses
#to_raise(exception) Raise an exception instead of returning a response
#to_timeout Raise TimeoutError simulating a request timeout
#call_count Number of times this stub has been matched
#called? Whether this stub has been matched at least once

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT