The goal is to create a test that validates the correct data is send to an external API without actually making the request.
I am trying to mock a chain a mix of properties and a method. Partly because it is how the Klaviyo API works and because of my custom ApiClient.
Demeter Chains And Fluent Interfaces doesn't work for me and it seems that it only works for methods. I have also tried to various combinations of set()
e.g. ->set('request, Klaviyo::class)
with no luck.
So the chain looks like this:
$client
->request // Public property (could be amethod, but wouldn't address the "Profiles" property)
->Profiles // Public property - Klaviyo SDK
->subscribeProfiles($body) // Public method - Klaviyo SDK
How Klaviyo initialize Profiles.
Code examples:
use KlaviyoAPI\KlaviyoAPI;
class ApiClient
{
public readonly KlaviyoAPI $request;
public function __construct(
string $apiKey,
private int $retries = 3,
private int $waitSeconds = 3,
private array $guzzleOptions = [],
)
{
$this->request = new KlaviyoAPI(
$apiKey,
$this->retries,
$this->waitSeconds,
$this->guzzleOptions,
);
}
}
use ApiClient;
class Newsletter
{
public function resubscribe(ApiClient $client, string $listId, string $email)
{
$body = [
'data' => [
'type' => 'profile-subscription-bulk-create-job',
'attributes' => [
'list_id' => $listId,
'custom_source' => 'Resubscribe user',
'subscriptions' => [
[
'channels' => [
'email' => [
'MARKETING',
],
],
'email' => $email,
],
],
],
],
];
return $client->request->Profiles->subscribeProfiles($body);
}
}
use ApiClient;
use Newsletter;
use Mockery;
public function testSubscribeUser(): void
{
$apiKey = 'api_key_12345';
$listId = 'list_id_12345';
$email = 'test@test.com';
$body = [
'data' => [
'type' => 'profile-subscription-bulk-create-job',
'attributes' => [
'list_id' => $listId,
'custom_source' => 'Resubscribe user',
'subscriptions' => [
[
'channels' => [
'email' => [
'MARKETING',
],
],
'email' => $email,
],
],
],
],
];
// This does not validate that "$body" is correct and still makes a request to the Klaviyo API
$client = Mockery::mock(ApiClient::class, [$apiKey]);
$response = resolve(Newsletter::class)->resubscribe($client, $listId, $email);
static::assertNull($response); // When succesful "null" is returned
// I am trying to use Demeter Chains And Fluent Interfaces
// but it doesn't work since I am mixing properties and methods
// and I get the error "ErrorException: Undefined property: Mockery\CompositeExpectation::$request"
$client = Mockery::mock(ApiClient::class, [$apiKey]);
$client->shouldReceive('request->Profiles->subscribeProfiles')
->with($body) // I assume this is how I validate "$body"
->once()
->andReturnNull();
$response = resolve(Newsletter::class)->resubscribe($client, $listId, $email);
static::assertNull($response); // When succesful "null" is returned
}