REST API Testing
PHPUnit REST API Tests
namespace Vendor\Module\Test\Api;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
class ProductApiTest extends TestCase
{
private $apiClient;
private $token;
protected function setUp(): void
{
$this->apiClient = Bootstrap::getApiClient();
$this->token = $this->getAdminToken();
}
private function getAdminToken(): string
{
$response = $this->apiClient->post('/rest/V1/integration/admin/token', [
'body' => json_encode([
'username' => 'admin',
'password' => 'admin123',
]),
'headers' => ['Content-Type' => 'application/json'],
]);
return json_decode($response->getBody(), true);
}
public function testGetProducts()
{
$response = $this->apiClient->get('/rest/V1/products', [
'headers' => [
'Authorization' => 'Bearer ' . $this->token,
'Content-Type' => 'application/json',
],
]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertArrayHasKey('items', $data);
$this->assertArrayHasKey('total_count', $data);
}
public function testCreateProduct()
{
$productData = [
'product' => [
'sku' => 'API-TEST-001',
'name' => 'API Test Product',
'price' => 29.99,
'status' => 1,
'visibility' => 4,
'type_id' => 'simple',
'attribute_set_id' => 4,
],
];
$response = $this->apiClient->post('/rest/V1/products', [
'body' => json_encode($productData),
'headers' => [
'Authorization' => 'Bearer ' . $this->token,
'Content-Type' => 'application/json',
],
]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertEquals('API-TEST-001', $data['sku']);
}
}
Key Points
- Use admin tokens for authenticated requests
- Test CRUD operations for each resource
- Verify response status codes and body structure
- Clean up test data after tests
GraphQL Testing
GraphQL Query Tests
class GraphqlProductTest extends TestCase
{
public function testGetProductBySku()
{
$query = <<<GRAPHQL
{
products(filter: { sku: { eq: "TEST-PRODUCT-001" } }) {
items {
name
sku
price {
regularPrice {
amount {
value
currency
}
}
}
}
}
}
GRAPHQL;
$response = $this->sendGraphqlQuery($query);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertArrayHasKey('data', $data);
$this->assertCount(1, $data['data']['products']['items']);
$this->assertEquals('TEST-PRODUCT-001', $data['data']['products']['items'][0]['sku']);
}
private function sendGraphqlQuery(string $query)
{
return $this->apiClient->post('/graphql', [
'body' => json_encode(['query' => $query]),
'headers' => ['Content-Type' => 'application/json'],
]);
}
}
GraphQL Mutation Tests
public function testAddToCart()
{
$mutation = <<<GRAPHQL
mutation {
addSimpleProductsToCart(
input: {
cart_id: "{$cartId}",
cart_items: [
{
data: {
quantity: 1
sku: "TEST-PRODUCT-001"
}
}
]
}
) {
cart {
items {
quantity
product {
name
}
}
}
}
}
GRAPHQL;
$response = $this->sendGraphqlQuery($mutation);
$data = json_decode($response->getBody(), true);
$this->assertCount(1, $data['data']['addSimpleProductsToCart']['cart']['items']);
}
Key Points
- GraphQL tests verify query and mutation responses
- Use variables for dynamic values
- Test error handling for invalid queries
- Verify nested data structures
API Test Fixtures
API Fixture Setup
class ApiTestCase extends TestCase
{
protected $fixtures = [];
protected function setUp(): void
{
$this->loadFixtures();
}
protected function loadFixtures()
{
$fixturesDir = __DIR__ . '/../fixtures/';
foreach ($this->fixtures as $fixtureFile) {
$fixtureData = require $fixturesDir . $fixtureFile;
$this->createFixture($fixtureData);
}
}
private function createFixture(array $data)
{
foreach ($data as $entityType => $entities) {
foreach ($entities as $entity) {
$this->apiClient->post("/rest/V1/{$entityType}", [
'body' => json_encode($entity),
'headers' => [
'Authorization' => 'Bearer ' . $this->token,
'Content-Type' => 'application/json',
],
]);
}
}
}
protected function tearDown(): void
{
$this->cleanFixtures();
}
private function cleanFixtures()
{
// Clean up test data
foreach ($this->createdEntities as $entity) {
$this->apiClient->delete("/rest/V1/{$entity['type']}/{$entity['id']}");
}
}
}
Fixture Data Files
// tests/_fixtures/products.json
[
{
"sku": "FIXTURE-001",
"name": "Fixture Product 1",
"price": 19.99,
"status": 1,
"visibility": 4,
"type_id": "simple",
"attribute_set_id": 4
}
]
Key Points
- Use fixtures for consistent test data
- Clean up fixtures after tests
- Keep fixtures minimal
- Version control fixture data
Assertion Patterns
Response Validation
class ApiResponseAssertions
{
public function assertSuccessResponse($response, $expectedData = null)
{
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertNotNull($data, 'Response is not valid JSON');
if ($expectedData) {
$this->assertArraySubset($expectedData, $data);
}
}
public function assertErrorResponse($response, $statusCode, $expectedMessage)
{
$this->assertEquals($statusCode, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertArrayHasKey('message', $data);
$this->assertStringContainsString($expectedMessage, $data['message']);
}
public function assertPaginatedResponse($response, $expectedTotal)
{
$data = json_decode($response->getBody(), true);
$this->assertArrayHasKey('items', $data);
$this->assertArrayHasKey('total_count', $data);
$this->assertEquals($expectedTotal, $data['total_count']);
}
}
Schema Validation
public function validateProductSchema($product)
{
$requiredFields = ['sku', 'name', 'price', 'status', 'visibility'];
foreach ($requiredFields as $field) {
$this->assertArrayHasKey($field, $product, "Missing field: {$field}");
}
$this->assertIsString($product['sku']);
$this->assertIsString($product['name']);
$this->assertIsNumeric($product['price']);
$this->assertGreaterThan(0, $product['price']);
}
Key Points
- Validate status codes, headers, and body
- Check response schema consistency
- Test both success and error cases
- Use reusable assertion methods
Practice Problems
0 / 1 solved
REST API Test Suite
Write a complete test suite for the customer REST API including CRUD operations.
Solution
<?php
namespace Vendor\Module\Test\Api;
use PHPUnit\Framework\TestCase;
class CustomerApiTest extends TestCase
{
private $token;
private $customerId;
protected function setUp(): void
{
$this->token = $this->getAdminToken();
}
public function testCreateCustomer()
{
$response = $this->apiClient->post('/rest/V1/customers', [
'body' => json_encode([
'customer' => [
'email' => 'test@example.com',
'firstname' => 'Test',
'lastname' => 'Customer',
],
'password' => 'Password123!',
]),
'headers' => $this->getAuthHeaders(),
]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->customerId = $data['id'];
$this->assertEquals('test@example.com', $data['email']);
}
public function testGetCustomer()
{
$response = $this->apiClient->get("/rest/V1/customers/{$this->customerId}", [
'headers' => $this->getAuthHeaders(),
]);
$this->assertEquals(200, $response->getStatusCode());
}
public function testUpdateCustomer()
{
$response = $this->apiClient->put("/rest/V1/customers/{$this->customerId}", [
'body' => json_encode([
'customer' => [
'firstname' => 'Updated',
],
]),
'headers' => $this->getAuthHeaders(),
]);
$this->assertEquals(200, $response->getStatusCode());
}
public function testDeleteCustomer()
{
$response = $this->apiClient->delete("/rest/V1/customers/{$this->customerId}", [
'headers' => $this->getAuthHeaders(),
]);
$this->assertEquals(200, $response->getStatusCode());
}
} Quiz
1. What does API testing verify?
2. How do you authenticate API tests?
3. What status code indicates successful creation?
4. Why test GraphQL queries?
Flashcards
Question
API test authentication?
Click to reveal answer
Answer
Bearer tokens from /rest/V1/integration/admin/token
Question
HTTP status for creation?
Click to reveal answer
Answer
201 Created
Question
GraphQL test focus?
Click to reveal answer
Answer
Query/mutation responses and data structure
Question
API test cleanup?
Click to reveal answer
Answer
Delete test data in tearDown()
Revision Notes
Key Takeaways
- 1. API tests verify endpoint behavior and data
- 2. Use Bearer tokens for authenticated requests
- 3. Test both REST and GraphQL endpoints
- 4. Validate response status codes and body structure
Interview Tips
- • Explain REST vs GraphQL testing differences
- • Discuss API test authentication
- • Know common HTTP status codes
Cheat Sheet
API Tests
- Auth: Bearer token
- REST: /rest/V1/{resource}
- GraphQL: /graphql
- Status: 200, 201, 400, 401, 404
- Validate: schema, data, errors