Skip to main content

Three lines

tests/Feature/BlogTest.php
Nothing is sent: no URL patterns, keys or JSON, and no config needed. Every call answers a valid model, and Attract::site('any-slug') is a faked site. It works the same in Pest.

Answers

Give a method its answer by key, <accessor>.<method>:

Every kind of answer

A model, a wire array, a closure fn (array $args, string $site) => …, an exception (thrown), or Attract::sequence($first, $second) (each call takes the next). blogPosts.* answers a whole accessor. A key that is no method throws, suggesting the closest one.
Attract::sequence($page1, $page2) takes each answer as its own argument (not an array); each call takes the next. One call past the end throws a LogicException: “The sequence for blogPosts.listBlogs ran out.” Give one answer per call you expect.
Attract::validationError([...]), notFound(), forbidden(), rateLimited(30), unreachable(), siteError(500): the real exception classes, for the faked site and route.
Every answer class has ::factory()->make([...]), makeMany(3) and raw() (the wire array), filled with values the SDK’s own answer check accepts. Override by property name, with SDK values (Carbon, a model, a Collection, an enum case) or as the API sends them: a date as an ISO 8601 string or now(), an enum as its string, a list as a plain array. A plain array for a nested model or a list of them is filled in over that model’s own example, so only what you name changes.
A listBlogsAll() walk is answered through blogPosts.listBlogs, a findBlog() through blogPosts.getBlog, request() through the key request. A job’s wait() needs the management SDK, under the fake too.

Your own values, nested

Fake one answer by key, override a nested field with a plain array, and assert the call:
tests/Feature/MediaTest.php
Each item of results is a plain array filled in over MediaImage’s example, like any nested model, so every field you don’t name is still valid.

Assertions

A failure lists what was called.

Scaffold an Action

app/Actions/Attract/GetBlog.php

Engine calls

Attract::fake() fakes the SDK’s methods, not the engine: an api() call under it throws, saying so. For those, use Http::fake() with Sleep::fake() (the retry waits go through Laravel’s Sleep), and a test key that keeps to a real key’s characters:

Sweeping a real site

It calls every read the site offers (each list, then up to three documents by id) and reports ok, refused or broken for each; it exits 1 when something broke. A health check.

Next: keeping keys secret

Where a key never shows up, and the one place it does.