Skip to content

Latest commit

 

History

History
243 lines (186 loc) · 5.98 KB

File metadata and controls

243 lines (186 loc) · 5.98 KB

Testing

This guide covers testing strategies for the SDK and your integration code.

Test Framework

The SDK uses PestPHP for testing:

composer require --dev pestphp/pest

Running Tests

# Run all tests
./vendor/bin/pest

# Run specific test file
./vendor/bin/pest tests/Unit/ClientTest.php

# Run with coverage
./vendor/bin/pest --coverage

Unit Testing with Guzzle MockHandler

The SDK uses Guzzle for HTTP transport, so the best way to test is using Guzzle's MockHandler.

use Directo\Client;
use Directo\Config;
use Directo\Http\Transporter;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

test('lists items', function () {
    // 1. Create the mock response
    $mockXml = <<<XML
    <?xml version="1.0" encoding="UTF-8"?>
        <transport>
            <items>
                <item code="ITEM001" name="Test Product" class="SERVICES">
                    <datafields/>
                </item>
            </items>
        </transport>
    XML;

    $mock = new MockHandler([
        new Response(200, [], $mockXml),
    ]);

    // 2. Wrap it in a HandlerStack and Guzzle Client
    $handlerStack = HandlerStack::create($mock);
    $httpClient = new GuzzleClient(['handler' => $handlerStack]);

    // 3. Create Config and Transporter
    $config = new Config(token: 'test-token');
    $transport = new Transporter($config, $httpClient);

    // 4. Create the Directo Client
    $client = new Client($config, $transport);

    $items = $client->items()->list();

    expect($items)->toBeArray();
    expect($items[0]['@code'])->toBe('ITEM001');
    expect($items[0]['@name'])->toBe('Test Product');
});

Testing PUT Operations

test('creates item', function () {
    $history = [];
    $historyMiddleware = Middleware::history($history);

    $mock = new MockHandler([
        new Response(200, [], '<results><ok/></results>'),
    ]);
    
    $handlerStack = HandlerStack::create($mock);
    $handlerStack->push($historyMiddleware);
    $httpClient = new GuzzleClient(['handler' => $handlerStack]);
    
    $config = new Config(token: 'test-token');
    $transport = new Transporter($config, $httpClient);
    $client = new Client($config, $transport);

    $result = $client->items()->put([
        '@attributes' => [
            'code' => 'ITEM001',
            'name' => 'New Product',
            'class' => 'SERVICES',
        ],
    ]);

    expect($result)->toHaveKey('ok');
    
    // Verify sent request body
    $sentBody = (string) $history[0]['request']->getBody();
    parse_str($sentBody, $params);

    expect($params['what'])->toBe('item');
    expect($params['xmldata'])->toContain('<item code="ITEM001" name="New Product" class="SERVICES">');
});

Testing Error Handling

use Directo\Exception\ApiErrorException;
use Directo\Exception\AuthenticationException;

test('handles auth error', function () {
    $errorXml = '<results><result type="5">Invalid token</result></results>';
    
    $mock = new MockHandler([
        new Response(200, [], $errorXml),
    ]);
    $httpClient = new GuzzleClient(['handler' => HandlerStack::create($mock)]);
    
    $config = new Config(token: 'test-token');
    $transport = new Transporter($config, $httpClient);
    $client = new Client($config, $transport);

    expect(fn() => $client->items()->list())
        ->toThrow(AuthenticationException::class); // Note: AuthenticationException needs to be implemented/mapped if you have specific logic for it, otherwise it might be ApiErrorException
});

Integration Testing

For real API testing (use sparingly):

test('integration: lists items from real API', function () {
    $token = getenv('DIRECTO_TOKEN');
    
    if (!$token) {
        $this->markTestSkipped('DIRECTO_TOKEN not set');
    }

    $client = new Client(new Config(token: $token));
    $items = $client->items()->list(['closed' => 0]);

    expect($items)->toBeArray();
})->group('integration');

Run integration tests:

DIRECTO_TOKEN=your-token ./vendor/bin/pest --group=integration

Test Helpers

Create reusable test helpers:

// tests/Helpers.php

use Directo\Client;
use Directo\Config;
use Directo\Http\Transporter;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

function mockClient(string $responseXml): Client
{
    $mock = new MockHandler([
        new Response(200, [], $responseXml),
    ]);
    $httpClient = new GuzzleClient(['handler' => HandlerStack::create($mock)]);
    
    $config = new Config(token: 'test-token');
    $transport = new Transporter($config, $httpClient);
    
    return new Client($config, $transport);
}

function itemsXml(array $items): string
{
    $xml = '<?xml version="1.0" encoding="UTF-8"?><results>';
    foreach ($items as $item) {
        $xml .= '<item>';
        foreach ($item as $key => $value) {
            $xml .= "<{$key}>{$value}</{$key}>";
        }
        $xml .= '</item>';
    }
    return $xml . '</results>';
}

Test Structure

Recommended test organization:

tests/
├── Pest.php
├── Helpers.php
├── Unit/
│   ├── ClientTest.php
│   ├── Parser/
│   │   ├── XmlResponseParserTest.php
│   │   ├── XmlRequestBuilderTest.php
│   │   └── ErrorResponseDetectorTest.php
│   ├── Endpoint/
│   │   ├── ItemsEndpointTest.php
│   │   └── CustomersEndpointTest.php
│   └── Schema/
│       └── SchemaRegistryTest.php
├── Feature/
│   ├── ListItemsTest.php
│   └── PutItemsTest.php
└── Integration/
    └── RealApiTest.php

Code Coverage

# Generate coverage report
./vendor/bin/pest --coverage --coverage-html=coverage

# Minimum coverage threshold
./vendor/bin/pest --coverage --min=80

See Also