Setting Up Continuous Integration for Laravel Packages
Laravel package maintainers need tests that check how their code behaves inside Laravel, across the PHP and Laravel versions the package supports.…
When Laravel code sends an HTTP request to an external API, a test that calls the real service relies on both the network and the service being available. Laravel’s HTTP client fakes make Laravel return a response you define, so your test can run the code that sends the request without contacting the provider.
A fake response is an answer you set for a test, such as a JSON body and HTTP status code. After your code runs, you can inspect the request Laravel recorded and check its URL, method, headers, and payload. The recorded request shows what your code tried to send, but it cannot establish whether the external provider received or accepted it.
Register the fake before running the service, job, or other code that sends the HTTP request. The example assumes OrderService::createOrder() sends a POST request to the API. The endpoint and payload are examples.
use App\Services\OrderService;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
public function test_it_sends_the_expected_order_request(): void
{
Http::preventStrayRequests();
Http::fake([
'https://api.example.test/v1/orders' => Http::response(
['id' => 'ord_123'],
201
),
]);
app(OrderService::class)->createOrder([
'sku' => 'ABC-123',
'quantity' => 2,
]);
Http::assertSent(function (Request $request) {
return $request->method() === 'POST'
&& $request->url() === 'https://api.example.test/v1/orders'
&& $request->hasHeader('Authorization', 'Bearer test-token')
&& $request->data() === [
'sku' => 'ABC-123',
'quantity' => 2,
];
});
Http::assertSentCount(1);
}
Http::fake() pairs a URL pattern with a response. Laravel’s Http::response() helper lets you set the response body and status code. In this example, the service gets a 201 response containing an order ID instead of calling the real API.
The test also calls Http::preventStrayRequests(). A fake for one URL handles only requests to matching URLs; an unmatched request can otherwise reach the network. This guard makes an unmatched HTTP-client request throw an exception. You can instead provide a '*' fallback fake to give every unmatched URL a defined response.
For each recorded request, Http::assertSent() receives a callback, a function Laravel runs to check that request. The callback returns true when the request matches your expectations. Laravel passes the function a request object with methods for checking the URL, method, headers, and data. See the request API for those methods.
data() returns the request data in structured or JSON form, so the assertion checks the payload as an array. Comparing the arrays for exact equality checks that all expected fields are present and no extra fields are included. To check only selected values, use array access, such as $request['sku'] === 'ABC-123'. For a header, hasHeader() checks both its name and value.
Use Http::assertSentCount(1) when the test should send exactly one request. Http::assertNotSent() checks that Laravel did not make a matching request. Http::assertNothingSent() checks that Laravel made no HTTP-client requests. Laravel’s Http::recorded() lets you inspect the recorded requests and their responses.
Choose a fake response based on how your code should handle it. For example, return an error status and body to test how the service handles a failed API response. When your code makes several calls, Laravel supports response sequences, which give each request the next configured response.
A test that uses a fake checks how your code builds a request and handles the response you configured. It does not check how the live provider behaves, including whether it accepts the payload or returns the same response. Keep request assertions focused on what your code controls. Use a separate integration check to test communication with the real provider.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com