Skip to content
intermediate Phase 73 · Testing Fundamentals

API Tests

45m
1 problems
Topic Progress 0%

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?

Question 1 options

2. How do you authenticate API tests?

Question 2 options

3. What status code indicates successful creation?

Question 3 options

4. Why test GraphQL queries?

Question 4 options

Flashcards

Question

API test authentication?

Answer

Bearer tokens from /rest/V1/integration/admin/token

Question

HTTP status for creation?

Answer

201 Created

Question

GraphQL test focus?

Answer

Query/mutation responses and data structure

Question

API test cleanup?

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