Skip to content

Spies, Noops & MockHttpClient

In addition to MockHttpAdapter, the testing sub-path ships lower-level helpers for testing specific parts of your pipeline in isolation.

MockHttpClient

A lower-level test double for the HttpClientContract transport layer. Use it when testing custom HttpClient wrappers rather than full adapter pipelines.

It exposes the same queue API and assertion helpers as MockHttpAdapter:

typescript
import { MockHttpClient } from '@yildizpay/http-adapter/testing';

const client = new MockHttpClient();
client.mockResolvedValue({ data: { id: 1 }, status: 200, headers: {} });

const result = await client.request(config);

client.assertCalledTimes(1);
client.assertCalledWith({ method: HttpMethod.POST });

Noop Helpers

Pass-through implementations that satisfy a contract without any side effects. Useful when a hook must be provided but its behaviour is irrelevant to the test under execution.

ClassImplements
NoopInterceptorAll four HttpInterceptor hooks — returns each value unchanged
NoopObserverAll HttpAdapterObserver hooks — empty methods
NoopCircuitBreakerObserverAll CircuitBreakerObserver hooks — empty methods
typescript
import {
  NoopInterceptor,
  NoopObserver,
  NoopCircuitBreakerObserver,
} from '@yildizpay/http-adapter/testing';

const adapter = HttpAdapter.builder()
  .withInterceptor(new NoopInterceptor())
  .withObserver(new NoopObserver())
  .build();

Spy Helpers

Spy helpers record every invocation and pass values through unchanged. Use them to assert that a hook was called without needing to mock the full request pipeline.

SpyInterceptor

Records calls to all four interceptor hooks:

typescript
import { SpyInterceptor } from '@yildizpay/http-adapter/testing';

const interceptorSpy = new SpyInterceptor();

const adapter = HttpAdapter.builder()
  .withInterceptor(interceptorSpy)
  .build();

await adapter.send(request);

expect(interceptorSpy.requestCalls).toHaveLength(1);
expect(interceptorSpy.responseCalls).toHaveLength(1);
expect(interceptorSpy.errorCalls).toHaveLength(0);

// Reset between tests
interceptorSpy.reset();

SpyObserver

Records calls to all observer hooks:

typescript
import { SpyObserver } from '@yildizpay/http-adapter/testing';

const observerSpy = new SpyObserver();

const adapter = HttpAdapter.builder()
  .withObserver(observerSpy)
  .build();

await adapter.send(request);

expect(observerSpy.requestStartCalls).toHaveLength(1);
expect(observerSpy.successCalls[0].durationMs).toBeGreaterThan(0);
expect(observerSpy.failureCalls).toHaveLength(0);
expect(observerSpy.retryCalls).toHaveLength(0);

observerSpy.reset();

Recorded Arrays Reference

SpyRecorded arrays
SpyInterceptorrequestCalls, responseCalls, responseValidatedCalls, errorCalls
SpyObserverrequestStartCalls, successCalls, failureCalls, retryCalls

Putting It All Together

A common pattern in integration tests: use SpyInterceptor and SpyObserver alongside MockHttpAdapter to assert the full pipeline was exercised correctly.

typescript
import {
  MockHttpAdapter,
  SpyInterceptor,
  SpyObserver,
} from '@yildizpay/http-adapter/testing';

const adapter = new MockHttpAdapter();
const interceptorSpy = new SpyInterceptor();
const observerSpy = new SpyObserver();

// Wire spies into a real adapter wrapping the mock transport
const wrappedAdapter = HttpAdapter.builder()
  .withHttpClient(adapter)         // inject mock transport
  .withInterceptor(interceptorSpy)
  .withObserver(observerSpy)
  .build();

adapter.mockResolvedValue({ id: 'order_1' });

await wrappedAdapter.send(request);

// Verify full pipeline was exercised
expect(interceptorSpy.requestCalls).toHaveLength(1);
expect(interceptorSpy.responseCalls).toHaveLength(1);
expect(observerSpy.successCalls).toHaveLength(1);

Released under the MIT License.