API Platform Testing
Edit on GitHubThis document describes how to write and run tests for your API Platform resources in your project.
Overview
API Platform provides a comprehensive testing infrastructure built on top of:
- Codeception: Test framework for PHP
- API Platform Test Client: Specialized HTTP client for API testing
- PHPUnit Assertions: Rich set of assertion methods
- Test Helpers: Custom helpers for test data management
The testing infrastructure supports both Backend and Storefront API types with dedicated base classes and configuration.
Test architecture
Test class hierarchy
AbstractApiTestCase (base class from core)
├── BackendApiTestCase (for Backend API tests)
└── StorefrontApiTestCase (for Storefront API tests)
Key components
| Component | Purpose |
|---|---|
AbstractApiTestCase |
Base class providing API Platform integration |
BackendApiTestCase |
Pre-configured for Backend API testing |
StorefrontApiTestCase |
Pre-configured for Storefront API testing |
ApiTestKernel |
Lightweight Symfony kernel for testing |
ApiTestAssertionsTrait |
API-specific assertions (from API Platform) |
Test helper classes
The testing infrastructure provides specialized Codeception helpers to streamline test development:
| Helper Class | Purpose |
|---|---|
BootstrapHelper |
Configures application plugin providers for test environments via codeception.yml. Allows different test suites to use different factory implementations without hardcoding dependencies in test infrastructure. |
ApiPlatformHelper |
Automatically cleans compiled Symfony test kernel cache after test suites complete. This ensures a clean state between test runs and prevents cache-related test failures. |
ApiPlatformConfigBuilder |
Provides a fluent interface for building test-specific API Platform configurations. Useful for creating isolated test scenarios with custom settings. |
ApiResourceGeneratorHelper |
Assists with testing resource generation functionality. Provides methods to generate test resources, validate generation output, and clean up generated files. |
These helpers are automatically available in your test cases through the Codeception actor and provide essential functionality for testing API Platform resources effectively.
Setting up your test environment
1. Configure autoloading for generated test resources
Update your project-level composer.json to include the test API namespace:
composer.json (project root)
{
"autoload-dev": {
"psr-4": {
"PyzTest\\": "tests/PyzTest/",
"Generated\\TestApi\\": "tests/_data/Api/"
}
}
}
2. Optional: Configure application plugin providers
If your tests require application plugins to be registered (for example, service providers or middleware), configure the BootstrapHelper in your suite’s codeception.yml:
tests/PyzTest/Glue/Customer/BackendApi/codeception.yml
modules:
enabled:
- \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
applicationPluginProvider:
class: Spryker\Glue\GlueBackendApiApplication\GlueBackendApiApplicationFactory
method: getApplicationPlugins
For Storefront API tests, use the appropriate factory:
tests/PyzTest/Glue/Customer/StorefrontApi/codeception.yml
modules:
enabled:
- \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
applicationPluginProvider:
class: Spryker\Glue\GlueStorefrontApiApplication\GlueStorefrontApiApplicationFactory
method: getApplicationPlugins
Configuration options:
class: The fully qualified class name of the factory that provides application pluginsmethod: The method name to call on the factory (typicallygetApplicationPlugins)
If no applicationPluginProvider is configured, the helper returns an empty array, and tests run without additional application plugins.
3. Create test directory structure
tests/
├── PyzTest/
│ └── Glue/
│ └── Customer/
│ ├── BackendApi/
│ │ ├── codeception.yml
│ │ └── CustomersBackendApiTest.php
│ └── StorefrontApi/
│ ├── codeception.yml
│ └── CustomersStorefrontApiTest.php
└── _data/
└── Api/
├── Backend/
│ └── CustomersBackendResource.php (generated)
└── Storefront/
└── CustomersStorefrontResource.php (generated)
4. Generate API resources for testing
The resources and the container are automatically generated right before the test suite runs.
Automatic resource generation and cleanup
The test infrastructure handles resource lifecycle automatically:
- Generation: Test-specific API resources are generated into
tests/_data/Api/{ApiType}/before tests execute - Cleanup: The
ApiPlatformHelperautomatically cleans the compiled Symfony test kernel cache after test suites complete - Isolation: Each test suite gets a fresh cache state, preventing cross-test contamination
This automation ensures that:
- Tests always run against the latest schema definitions
- No manual cache clearing is required between test runs
- Test failures related to stale cache are eliminated
Writing Backend API tests
Basic test structure
Backend API tests extend BackendApiTestCase and use the BackendApiTester tester which gets automatically injected into your tests by Codeception.
tests/PyzTest/Glue/Customer/BackendApi/CustomersBackendApiTest.php
<?php
namespace PyzTest\Glue\Customer\BackendApi;
use PyzTest\Glue\Customer\BackendApiTester;
use SprykerTest\Shared\ApiPlatform\Test\BackendApiTestCase;
/**
* @group PyzTest
* @group Glue
* @group Customer
* @group BackendApi
* @group CustomersBackendApiTest
*/
class CustomersBackendApiTest extends BackendApiTestCase
{
protected BackendApiTester $tester;
public function testGivenValidDataWhenCreatingCustomerViaPostThenCustomerIsCreatedSuccessfully(): void
{
// Arrange
$customerData = [
'email' => '[email protected]',
'firstName' => 'John',
'lastName' => 'Doe',
];
// Act
static::createClient()->request('POST', '/customers', ['json' => $customerData]);
// Assert
$this->assertResponseIsSuccessful();
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains(['email' => '[email protected]']);
$this->assertJsonContains(['firstName' => 'John']);
$this->assertJsonContains(['lastName' => 'Doe']);
}
}
Testing GET operations
Single resource
public function testGivenExistingCustomerWhenRetrievingViaGetThenCustomerDataIsReturned(): void
{
// Arrange
$customerTransfer = $this->tester->haveCustomer([
'email' => '[email protected]',
'firstName' => 'Jane',
'lastName' => 'Smith',
]);
// Act
static::createClient()->request(
'GET',
sprintf('/customers/%s', $customerTransfer->getCustomerReference())
);
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['email' => '[email protected]']);
$this->assertJsonContains(['firstName' => 'Jane']);
}
Collection with pagination
public function testGivenMultipleCustomersWhenRetrievingCollectionViaGetThenAllCustomersAreReturned(): void
{
// Arrange
$this->tester->haveCustomer(['email' => '[email protected]']);
$this->tester->haveCustomer(['email' => '[email protected]']);
$this->tester->haveCustomer(['email' => '[email protected]']);
// Act
static::createClient()->request('GET', '/customers');
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['@type' => 'Collection']);
$this->assertJsonContains(['totalItems' => 3]);
}
public function testGivenPaginationParamsWhenRetrievingCollectionThenPaginatedResultsAreReturned(): void
{
// Arrange
for ($i = 1; $i <= 15; $i++) {
$this->tester->haveCustomer(['email' => sprintf('customer%[email protected]', $i)]);
}
// Act
static::createClient()->request('GET', '/customers?page=2&itemsPerPage=5');
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['@type' => 'Collection']);
$this->assertJsonContains(['view' => ['@id' => '/customers?page=2&itemsPerPage=5']]);
}
Testing POST operations
Successful creation
public function testGivenValidDataWhenCreatingCustomerViaPostThenCustomerIsCreatedSuccessfully(): void
{
// Arrange
$customerData = [
'email' => '[email protected]',
'firstName' => 'New',
'lastName' => 'Customer',
];
// Act
$response = static::createClient()->request('POST', '/customers', [
'json' => $customerData,
]);
// Assert
$this->assertResponseIsSuccessful();
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains($customerData);
$this->assertResponseHeaderSame('Content-Type', 'application/ld+json; charset=utf-8');
// Verify the resource was created and has an ID
$responseData = $response->toArray();
$this->assertArrayHasKey('customerReference', $responseData);
$this->assertNotEmpty($responseData['customerReference']);
}
Validation errors
public function testGivenInvalidDataWhenCreatingCustomerViaPostThenValidationErrorIsReturned(): void
{
// Arrange
$invalidCustomerData = [
'email' => 'invalid-email', // Invalid email format
'firstName' => '', // Empty first name
];
// Act
static::createClient()->request('POST', '/customers', [
'json' => $invalidCustomerData,
]);
// Assert
$this->assertResponseStatusCodeSame(422);
$this->assertResponseHeaderSame('Content-Type', 'application/ld+json; charset=utf-8');
$this->assertJsonContains(['@type' => 'ConstraintViolationList']);
$this->assertJsonContains([
'violations' => [
['propertyPath' => 'email'],
['propertyPath' => 'firstName'],
['propertyPath' => 'lastName'],
],
]);
}
Business rule violations
public function testGivenDuplicateEmailWhenCreatingCustomerViaPostThenErrorIsReturned(): void
{
// Arrange
$this->tester->haveCustomer(['email' => '[email protected]']);
$duplicateData = [
'email' => '[email protected]',
'firstName' => 'Duplicate',
'lastName' => 'Customer',
];
// Act
static::createClient()->request('POST', '/customers', [
'json' => $duplicateData,
]);
// Assert
$this->assertResponseStatusCodeSame(422);
$this->assertJsonContains(['@type' => 'Error']);
$this->assertJsonContains(['detail' => 'Customer with this email already exists']);
}
Testing PATCH operations
public function testGivenExistingCustomerWhenUpdatingViaPatchThenCustomerIsUpdatedSuccessfully(): void
{
// Arrange
$customerTransfer = $this->tester->haveCustomer([
'email' => '[email protected]',
'firstName' => 'Original',
'lastName' => 'Name',
]);
$updateData = [
'firstName' => 'Updated',
'lastName' => 'Name',
];
// Act
static::createClient()->request(
'PATCH',
sprintf('/customers/%s', $customerTransfer->getCustomerReference()),
[
'json' => $updateData,
'headers' => [
'Content-Type' => 'application/merge-patch+json',
],
]
);
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['firstName' => 'Updated']);
$this->assertJsonContains(['email' => '[email protected]']); // Unchanged
}
Testing DELETE operations
public function testGivenExistingCustomerWhenDeletingViaDeleteThenCustomerIsDeletedSuccessfully(): void
{
// Arrange
$customerTransfer = $this->tester->haveCustomer([
'email' => '[email protected]',
]);
// Act
static::createClient()->request(
'DELETE',
sprintf('/customers/%s', $customerTransfer->getCustomerReference())
);
// Assert
$this->assertResponseStatusCodeSame(204);
$this->assertResponseHasNoContent();
}
public function testGivenNonExistentCustomerWhenDeletingViaDeleteThen404IsReturned(): void
{
// Act
static::createClient()->request('DELETE', '/customers/NON-EXISTENT-REFERENCE');
// Assert
$this->assertResponseStatusCodeSame(404);
}
Writing Storefront API tests
Basic test structure
Storefront API tests extend StorefrontApiTestCase and typically use mocks for read-only operations.
tests/PyzTest/Glue/Customer/StorefrontApi/CustomersStorefrontApiTest.php
<?php
namespace PyzTest\Glue\Customer\StorefrontApi;
use Codeception\Stub;
use Pyz\Client\Customer\CustomerClientInterface;
use PyzTest\Glue\Customer\StorefrontApiTester;
use SprykerTest\Shared\ApiPlatform\Test\StorefrontApiTestCase;
/**
* @group PyzTest
* @group Glue
* @group Customer
* @group StorefrontApi
* @group CustomersStorefrontApiTest
*/
class CustomersStorefrontApiTest extends StorefrontApiTestCase
{
protected StorefrontApiTester $tester;
public function testGivenAuthenticatedCustomerWhenRetrievingProfileViaGetThenCustomerDataIsReturned(): void
{
// Arrange
$customerClientStub = Stub::makeEmpty(CustomerClientInterface::class, [
'getCustomer' => (new CustomerTransfer())
->setEmail('[email protected]')
->setFirstName('John')
->setLastName('Doe'),
]);
static::getContainer()->set(CustomerClientInterface::class, $customerClientStub);
// Act
static::createClient()->request('GET', '/customers/me');
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['email' => '[email protected]']);
}
}
Testing with service mocks
public function testGivenMultipleCustomersWhenRetrievingCollectionViaGetThenAllCustomersAreReturned(): void
{
// Arrange
$customerClientStub = Stub::makeEmpty(CustomerClientInterface::class, [
'getCustomerCollection' => [
(new CustomerTransfer())->setEmail('[email protected]'),
(new CustomerTransfer())->setEmail('[email protected]'),
],
]);
static::getContainer()->set(CustomerClientInterface::class, $customerClientStub);
// Act
static::createClient()->request('GET', '/customers');
// Assert
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['@type' => 'Collection']);
}
Available assertions
HTTP response assertions
// Status codes
$this->assertResponseIsSuccessful(); // 2xx status code
$this->assertResponseStatusCodeSame(200); // Exact status code
$this->assertResponseStatusCodeSame(201); // Created
$this->assertResponseStatusCodeSame(204); // No content
$this->assertResponseStatusCodeSame(400); // Bad request
$this->assertResponseStatusCodeSame(401); // Unauthorized
$this->assertResponseStatusCodeSame(403); // Forbidden
$this->assertResponseStatusCodeSame(404); // Not found
$this->assertResponseStatusCodeSame(422); // Validation error
// Headers
$this->assertResponseHasHeader('Content-Type');
$this->assertResponseHeaderSame('Content-Type', 'application/ld+json; charset=utf-8');
$this->assertResponseHeaderNotSame('X-Custom-Header', 'value');
// Content
$this->assertResponseHasNoContent(); // Empty response body
JSON assertions
// Content matching
$this->assertJsonContains(['email' => '[email protected]']);
$this->assertJsonContains(['@type' => 'Customer']);
$this->assertJsonContains(['@type' => 'Collection']);
// Array keys
$responseData = $response->toArray();
$this->assertArrayHasKey('customerReference', $responseData);
$this->assertArrayNotHasKey('password', $responseData);
// Validation violations
$this->assertJsonContains(['@type' => 'ConstraintViolationList']);
$this->assertJsonContains([
'violations' => [
['propertyPath' => 'email'],
],
]);
// Collection metadata
$this->assertJsonContains(['totalItems' => 10]);
$this->assertJsonContains(['view' => ['@id' => '/customers?page=1']]);
Custom API Platform assertions
// JSON-LD context
$this->assertJsonContains(['@context' => '/contexts/Customer']);
// Hydra collections
$this->assertJsonContains(['hydra:totalItems' => 5]);
$this->assertJsonContains(['hydra:member' => []]);
// IRI matching
$iri = $this->getIriFromResource($resource);
$this->assertMatchesRegularExpression('~^/customers/[A-Z0-9\-]+$~', $iri);
Test data management
Using Codeception helpers
Create test data using your project’s tester helpers:
// Create a customer
$customerTransfer = $this->tester->haveCustomer([
'email' => '[email protected]',
'firstName' => 'John',
'lastName' => 'Doe',
]);
// Create multiple customers
for ($i = 1; $i <= 10; $i++) {
$this->tester->haveCustomer([
'email' => sprintf('customer%[email protected]', $i),
]);
}
Cleanup strategies
Automatic cleanup (default)
The test kernel automatically cleans up after each test. No manual cleanup needed.
Manual cleanup (when needed)
protected function tearDown(): void
{
// Custom cleanup logic
$this->tester->cleanupCustomers();
parent::tearDown();
}
Testing different media types
JSON-LD (default)
public function testJsonLdFormat(): void
{
static::createClient()->request('GET', '/customers', [
'headers' => [
'Accept' => 'application/ld+json',
],
]);
$this->assertResponseHeaderSame('Content-Type', 'application/ld+json; charset=utf-8');
$this->assertJsonContains(['@context' => '/contexts/Customer']);
}
JSON:API
public function testJsonApiFormat(): void
{
static::createClient()->request('GET', '/customers', [
'headers' => [
'Accept' => 'application/vnd.api+json',
],
]);
$this->assertResponseHeaderSame('Content-Type', 'application/vnd.api+json; charset=utf-8');
$this->assertJsonContains(['data' => ['type' => 'Customer']]);
}
HAL+JSON
public function testHalJsonFormat(): void
{
static::createClient()->request('GET', '/customers', [
'headers' => [
'Accept' => 'application/hal+json',
],
]);
$this->assertResponseHeaderSame('Content-Type', 'application/hal+json; charset=utf-8');
$this->assertJsonContains(['_links' => ['self' => ['href' => '/customers']]]);
}
Advanced testing patterns
Testing with filters
public function testGivenFilterParamsWhenRetrievingCollectionThenFilteredResultsAreReturned(): void
{
// Arrange
$this->tester->haveCustomer(['email' => '[email protected]', 'status' => 'active']);
$this->tester->haveCustomer(['email' => '[email protected]', 'status' => 'inactive']);
// Act
static::createClient()->request('GET', '/customers?status=active');
// Assert
$this->assertResponseIsSuccessful();
$responseData = static::createClient()->getResponse()->toArray();
$this->assertCount(1, $responseData['hydra:member']);
}
Testing sorting
public function testGivenSortParamsWhenRetrievingCollectionThenSortedResultsAreReturned(): void
{
// Arrange
$this->tester->haveCustomer(['lastName' => 'Zulu']);
$this->tester->haveCustomer(['lastName' => 'Alpha']);
$this->tester->haveCustomer(['lastName' => 'Bravo']);
// Act
static::createClient()->request('GET', '/customers?order[lastName]=asc');
// Assert
$this->assertResponseIsSuccessful();
$responseData = static::createClient()->getResponse()->toArray();
$members = $responseData['hydra:member'];
$this->assertEquals('Alpha', $members[0]['lastName']);
$this->assertEquals('Bravo', $members[1]['lastName']);
$this->assertEquals('Zulu', $members[2]['lastName']);
}
Testing error scenarios
public function testGivenMalformedJsonWhenCreatingCustomerViaPostThenBadRequestIsReturned(): void
{
// Act
static::createClient()->request('POST', '/customers', [
'body' => '{invalid-json}',
'headers' => [
'Content-Type' => 'application/json',
],
]);
// Assert
$this->assertResponseStatusCodeSame(400);
}
public function testGivenUnauthorizedRequestWhenAccessingProtectedResourceThen401IsReturned(): void
{
// Act
static::createClient()->request('GET', '/customers/me');
// Assert
$this->assertResponseStatusCodeSame(401);
}
Testing with authentication
public function testGivenAuthenticatedRequestWhenAccessingProtectedResourceThenDataIsReturned(): void
{
// Arrange
$token = $this->tester->haveAuthToken(['customer_reference' => 'CUST-123']);
// Act
static::createClient()->request('GET', '/customers/me', [
'headers' => [
'Authorization' => sprintf('Bearer %s', $token),
],
]);
// Assert
$this->assertResponseIsSuccessful();
}
Running tests
Run all project tests (slow, not recommended)
docker/sdk cli vendor/bin/codecept run
Run specific test suite
# Run Backend API tests only
docker/sdk cli vendor/bin/codecept run -g BackendApi
# Run Storefront API tests only
docker/sdk cli vendor/bin/codecept run -g StorefrontApi
Run with coverage
docker/sdk cli vendor/bin/codecept run --coverage --coverage-html
Codeception configuration
Suite configuration
Configure your test suite’s codeception.yml to enable the necessary helpers:
tests/PyzTest/Glue/Customer/BackendApi/codeception.yml
suite_namespace: PyzTest\Glue\Customer\BackendApi
actor: BackendApiTester
modules:
enabled:
- \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
applicationPluginProvider:
class: Spryker\Glue\GlueBackendApiApplication\GlueBackendApiApplicationFactory
method: getApplicationPlugins
paths:
tests: .
data: ../../../../../_data
support: _support
output: ../../../../../_output
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
Key configuration points:
- BootstrapHelper: Provides application plugins for the test kernel. This is optional and can be omitted if your tests do not require application-level dependencies.
- suite_namespace: Must match your test suite’s PHP namespace
- actor: The tester class name (for example,
BackendApiTester,StorefrontApiTester)
Helper classes
Create helper classes to manage test data:
tests/PyzTest/Glue/Customer/Helper/CustomerHelper.php
<?php
namespace PyzTest\Glue\Customer\Helper;
use Codeception\Module;
use Generated\Shared\Transfer\CustomerTransfer;
use Pyz\Zed\Customer\Business\CustomerFacadeInterface;
class CustomerHelper extends Module
{
public function haveCustomer(array $seed = []): CustomerTransfer
{
$customerTransfer = (new CustomerTransfer())
->fromArray($seed, true)
->setEmail($seed['email'] ?? sprintf('customer-%[email protected]', uniqid()))
->setFirstName($seed['firstName'] ?? 'Test')
->setLastName($seed['lastName'] ?? 'Customer');
return $this->getCustomerFacade()->createCustomer($customerTransfer);
}
protected function getCustomerFacade(): CustomerFacadeInterface
{
return $this->getModule('\\PyzTest\\Shared\\Testify\\Helper\\Environment')
->getFacade('Customer');
}
}
Best practices
1. Use descriptive test method names
// ✅ Good
public function testGivenInvalidEmailWhenCreatingCustomerViaPostThenValidationErrorIsReturned(): void
// ❌ Bad
public function testCreate(): void
2. Follow Arrange-Act-Assert pattern
public function testExample(): void
{
// Arrange - Set up test data and preconditions
$data = ['email' => '[email protected]'];
// Act - Execute the operation being tested
static::createClient()->request('POST', '/customers', ['json' => $data]);
// Assert - Verify the results
$this->assertResponseIsSuccessful();
}
3. Test one thing per test
// ✅ Good - Tests one specific validation rule
public function testGivenMissingEmailWhenCreatingCustomerThenValidationErrorIsReturned(): void
{
static::createClient()->request('POST', '/customers', ['json' => []]);
$this->assertJsonContains(['violations' => [['propertyPath' => 'email']]]);
}
// ❌ Bad - Tests multiple unrelated things
public function testCustomerCreation(): void
{
// Tests validation, creation, retrieval, update all in one test
}
4. Use meaningful test data
// ✅ Good
$customerData = [
'email' => '[email protected]', // Realistic email
'firstName' => 'John', // Realistic name
'lastName' => 'Doe',
];
// ❌ Bad
$customerData = [
'email' => '[email protected]', // Not realistic
'firstName' => 'x', // Not meaningful
'lastName' => 'y',
];
5. Clean up test data appropriately
// For Backend API tests - use tester helpers for setup
$customer = $this->tester->haveCustomer(['email' => '[email protected]']);
// Cleanup happens automatically via test kernel shutdown
6. Test error cases
// Always test both success and failure scenarios
public function testSuccessfulCreation(): void { /* ... */ }
public function testValidationErrors(): void { /* ... */ }
public function testDuplicateEmail(): void { /* ... */ }
public function testNotFound(): void { /* ... */ }
7. Use constants for repeated values
class CustomersBackendApiTest extends BackendApiTestCase
{
private const TEST_EMAIL = '[email protected]';
private const TEST_FIRST_NAME = 'John';
public function testExample(): void
{
$data = [
'email' => self::TEST_EMAIL,
'firstName' => self::TEST_FIRST_NAME,
];
// ...
}
}
8. Group related tests
/**
* @group PyzTest
* @group Glue
* @group Customer
* @group BackendApi
* @group CustomersBackendApiTest
* @group ValidationTests
*/
class CustomersBackendApiTest extends BackendApiTestCase
{
// Run only validation tests:
// vendor/bin/codecept run -g ValidationTests
}
Troubleshooting
Generated resources not found
Problem: Test fails with “Class not found” for generated resource.
Solution:
- Verify autoload configuration in
composer.json:
{
"autoload-dev": {
"psr-4": {
"PyzTest\\": "tests/PyzTest/",
"Generated\\TestApi\\": "tests/_data/Api/"
}
}
}
- Run composer dump-autoload:
docker/sdk cli composer dump-autoload
Test kernel boot failures
Problem: Tests fail with kernel boot errors.
Solution:
Ensure your test case extends the correct base class:
// For Backend API
use PyzTest\Shared\ApiPlatform\Test\BackendApiTestCase;
class CustomersBackendApiTest extends BackendApiTestCase
{
// ...
}
// For Storefront API
use PyzTest\Shared\ApiPlatform\Test\StorefrontApiTestCase;
class CustomersStorefrontApiTest extends StorefrontApiTestCase
{
// ...
}
Assertion failures with JSON-LD
Problem: JSON assertions fail with @context or @type fields.
Solution:
Use JSON-LD specific assertions:
// ✅ Correct
$this->assertJsonContains(['@type' => 'Customer']);
$this->assertJsonContains(['@context' => '/contexts/Customer']);
// ❌ Wrong
$this->assertJsonContains(['type' => 'Customer']);
Tester helper not found
Problem: $this->tester property shows as undefined.
Solution:
- Verify your tester class exists in the correct location
- Check that the tester is properly type-hinted in your test:
class CustomersBackendApiTest extends BackendApiTestCase
{
protected BackendApiTester $tester; // Must be declared
}
- Rebuild Codeception actors:
docker/sdk cli vendor/bin/codecept build
Next steps
- API Platform Enablement - Creating API resources
- Resource Schemas - Resource schema reference
- Validation Schemas - Validation schema reference
- Troubleshooting - Common issues and solutions
- Codeception Documentation - Codeception framework docs
- API Platform Testing - Official API Platform testing guide
Thank you!
For submitting the form