saloon icon indicating copy to clipboard operation
saloon copied to clipboard

Cached requests ignore fixture changes in tests

Open tovitch opened this issue 4 months ago • 1 comments

Description

When using cached requests with fixtures in tests, the cache persists even when MockClient fixtures are changed between test runs, causing tests to receive cached responses instead of the updated mock responses.

This happens because MockClient::destroyGlobal() clears the mock client but doesn't clear the caching layer, so the second fixture is ignored.

Expected Behavior

When using MockClient::fixture() or MockClient::global() in tests, fixtures should bypass or automatically disable caching to ensure tests use the mocked responses consistently.

Actual Behavior

Cached responses are returned even when different fixtures are set up, requiring manual cache disabling with disableCaching() in the constructor.

Code Example

// This fails because second run uses cached response from first fixture
test('cache ignores fixture changes', function () {
    // First run with fixture A
    MockClient::global([
        MyRequest::class => MockResponse::fixture('fixture-a'),
    ]);
    $response1 = $connector->send(new MyRequest()); // Uses fixture-a
    
    // Second run with fixture B - cache prevents this from working
    MockClient::destroyGlobal();
    MockClient::global([
        MyRequest::class => MockResponse::fixture('fixture-b'), 
    ]);
    $response2 = $connector->send(new MyRequest()); // Still uses fixture-a from cache!
});

Current Workaround

Add cache disabling in the request constructor:

public function __construct()
{
    if (app()->runningUnitTests()) {
        $this->disableCaching();
    }
}

Suggested Solution

  1. Option A: Fixtures should automatically bypass caching
  2. Option B: MockClient::global() and MockClient::destroyGlobal() should clear relevant caches when fixtures are set up
  3. Option C: Add a method like MockClient::globalWithoutCache() for testing scenarios

Environment

  • Saloon version: v3.14.0
  • Laravel version: 12.24.0
  • PHP version: 8.4

Related

This is related to the global mock client introduced in #359, where destroyGlobal() was added to prevent leaky tests, but the caching layer isn't cleared alongside the mock client.

tovitch avatar Aug 28 '25 18:08 tovitch

Can you try the following instead


// This fails because second run uses cached response from first fixture
test('cache ignores fixture changes', function () {
    // First run with fixture A
    MockClient::global([
        MyRequest::class => MockResponse::fixture('fixture-a'),
    ]);
    $request = new MyRequest(); 
    $request->disableCaching();();
    
    $response1 = $connector->send($request); // Uses fixture-a
    
    // Second run with fixture B - cache prevents this from working
    MockClient::destroyGlobal();
    MockClient::global([
        MyRequest::class => MockResponse::fixture('fixture-b'), 
    ]);

   
    $response2 = $connector->send($request); // This will use feature-b
});

Or you could manually clear the cache between tests using $request->invalidateCache()

Or you could use time traveling in your test to travel past the cache expiry.

craigpotter avatar Oct 21 '25 00:10 craigpotter