When developing applications in Rails, it is common to interact with external APIs such as payment gateway or notification services. Testing these applications should prevent actual HTTP calls to external services. Relying on actual HTTP requests can introduce issues such as server downtime or network erros which may end up in flaky tests and performance issues. Instead we can mock external service responses in the test environment using Webmock.
What is Webmock?
Webmock is a library for stubbing and setting expectations on HTTP requests in Ruby. It ensures that no external HTTP requests are made in test environment unless it is allowed explicitly.
How to stub requests with Webmock?
Consider a scenario where an endpoint in the Rails app fetches a set of todos from the JSONPlaceholder API
Here's how you can test this endpoint using WebMock and RSpec
What is Rack?
A Rack application is a simple interface that responds to #call
and takes a hash as an argument, returning an array of status,
headers and a body. The body needs to respond to #each
and then return strings that represent the response body.
Example of a basic Rack application:
How to use Rack with Webmock?
- Create a Rack compatible class
Implement a class that acts as a Rack application and returns responses based on request details.
Here's an example that stubs the /todos
endpoint
- Integrate the stub in Rails Tests
In rails_helper.rb
, add the following configuration to stub all requests to
jsonplaceholder.typicode.com
with the custom Rack app
- Write Your Test
Now, you can write your RSpec test as if the request is hitting the actual external service
Why Combine WebMock with Rack?
Using WebMock with Rack enables you to create a mock server-like behavior that dynamically processes HTTP requests
and returns flexible responses. While stub_request
is great for simple, static responses,
combining it with Rack allows for more complex and dynamic testing scenarios, such as:
- Simulating conditional responses based on request parameters or headers.
- Mimicking real-world server behavior that may involve custom status codes, headers, or logic.
- Reusability, allowing you to use the same Rack-based logic in multiple tests for consistency.
This approach enhances your tests by making them more robust, and reflects actual interactions with external services.
How is Webmock with Rack better than VCR?
VCR gem records the HTTP interactions during initial test run and replays them in subsequent test runs. Unlike VCR, WebMock with Rack does not require real HTTP requests at any stage, as it fully mocks the external service which is beneficial if the external APIs are unstable.