240 lines
6.9 KiB
Markdown
240 lines
6.9 KiB
Markdown
# Testing with Mollie API Client
|
|
|
|
## Test Mode Configuration
|
|
|
|
### Key Concepts
|
|
- Test mode is automatically determined by API key prefix (`test_` or `live_`)
|
|
- Explicit `testmode` parameter available for OAuth scenarios
|
|
- Configure at global client level or per individual request
|
|
|
|
### Global Configuration
|
|
```php
|
|
use Mollie\Api\MollieApiClient;
|
|
|
|
$mollie = new MollieApiClient();
|
|
$mollie->setApiKey("test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM");
|
|
$mollie->test(true); // Applies to all subsequent requests
|
|
```
|
|
|
|
### Per-Request Configuration
|
|
```php
|
|
// Payment request with test mode
|
|
$createPaymentRequest = new CreatePaymentRequest(/* ... */);
|
|
$mollie->send($createPaymentRequest->test(true));
|
|
|
|
// Endpoint collection example
|
|
$customer = $mollie->customers->get('cust_12345678', testmode: true);
|
|
```
|
|
|
|
## API Mocking
|
|
|
|
### Basic Usage
|
|
API responses can be simulated without network calls by using the `MollieApiClient::fake()` method. Each Request/Response pair passed to the fake method is consumed after a matching request.
|
|
|
|
> [!NOTE]
|
|
> To keep pairs available for reuse after they've been matched, use the `retainRequests` parameter: `MollieApiClient::fake([...], retainRequests: true)`
|
|
|
|
```php
|
|
use Mollie\Api\MollieApiClient;
|
|
use Mollie\Api\Fake\MockResponse;
|
|
use Mollie\Api\Http\Requests\GetPaymentRequest;
|
|
|
|
$client = MollieApiClient::fake([
|
|
GetPaymentRequest::class => new MockResponse(
|
|
body: [
|
|
'resource' => 'payment',
|
|
'id' => 'tr_xxxxxxxxxxxx',
|
|
'mode' => 'test',
|
|
'amount' => [
|
|
'value' => '20.00',
|
|
'currency' => 'EUR'
|
|
],
|
|
'description' => 'Test',
|
|
'status' => 'open',
|
|
// ...
|
|
],
|
|
status: 200
|
|
)
|
|
]);
|
|
|
|
$payment = $client->send(new GetPaymentRequest('tr_xxxxxxxxxxxx'));
|
|
```
|
|
|
|
### Sequence Mock Responses
|
|
To return different responses for the same request type, use `SequenceMockResponse` and pass `MockResponse` or `Closure` (which return `MockResponse` instances) in the order they should occur.
|
|
|
|
```php
|
|
$client = MollieApiClient::fake([
|
|
DynamicGetRequest::class => new SequenceMockResponse(
|
|
MockResponse::ok(['first' => 'response']),
|
|
MockResponse::ok(['second' => 'response']),
|
|
function (PendingRequest $pendingRequest) {
|
|
//...
|
|
|
|
return MockResponse::ok(['third' => 'response']);
|
|
}
|
|
)
|
|
])
|
|
|
|
To verify that a request was sent, use `assertSent` or `assertSentCount`.
|
|
|
|
```php
|
|
$client->send(new GetPaymentRequest('tr_xxxxxxxxxxxx', embedRefunds: true));
|
|
|
|
$client->assertSent(GetPaymentRequest::class);
|
|
$client->assertSent(function (PendingRequest $pendingRequest, Response $response) {
|
|
return $pendingRequest->query()->get('embed') === 'refunds';
|
|
});
|
|
|
|
$client->assertSentCount(1);
|
|
```
|
|
|
|
### MockResponse Options
|
|
Configure responses using:
|
|
- **Arrays**: Direct data structure
|
|
- **Strings**: JSON payloads or predefined fixture names
|
|
- **Callables**: Dynamic response generation *or* intercepting assertions
|
|
|
|
```php
|
|
// Array response
|
|
MockResponse::created([
|
|
'id' => 'tr_xxxxxxxxxxxx',
|
|
'amount' => ['value' => '20.00', 'currency' => 'EUR']
|
|
]);
|
|
|
|
// Fixture response
|
|
MockResponse::created('payment');
|
|
|
|
// Dynamic response
|
|
MockResponse::created(function (PendingRequest $request) {
|
|
return ['amount' => $request->hasParameter('amount') ? 10 : 20];
|
|
});
|
|
|
|
// Intercepting assertions
|
|
function (PendingRequest $request) use ($idempotencyKey) {
|
|
$this->assertEquals($idempotencyKey, $request->headers()->get('Idempotency-Key'));
|
|
|
|
return MockResponse::created('payment');
|
|
}
|
|
```
|
|
|
|
### Working with Collections
|
|
Create paginated list responses:
|
|
|
|
```php
|
|
use Mollie\Api\Resources\PaymentCollection;
|
|
|
|
$client = MollieApiClient::fake([
|
|
GetPaginatedPaymentsRequest::class => MockResponse::list(PaymentCollection::class)
|
|
->add([
|
|
'resource' => 'payment',
|
|
'id' => 'tr_xxxxxxxxxxxx',
|
|
'mode' => 'test',
|
|
'amount' => [
|
|
'value' => '20.00',
|
|
'currency' => 'EUR'
|
|
],
|
|
'description' => 'Test',
|
|
'status' => 'open',
|
|
// ...
|
|
])
|
|
->create()
|
|
]);
|
|
```
|
|
|
|
### Handling Embedded Resources
|
|
Simulate HAL+JSON embedded resources using the `_embedded` property:
|
|
|
|
**Key Concepts**
|
|
- Use `MockResponse::resource()` to start building a resource response
|
|
- Chain `embed()` calls to add related collections
|
|
- Maintain resource relationships with a fluent interface
|
|
|
|
```php
|
|
use Mollie\Api\Resources\Payment;
|
|
use Mollie\Api\Resources\RefundCollection;
|
|
use Mollie\Api\Resources\ChargebackCollection;
|
|
|
|
$client = MollieApiClient::fake([
|
|
GetPaymentRequest::class => MockResponse::resource(Payment::class)
|
|
->with([ // Main resource properties
|
|
'resource' => 'payment',
|
|
'id' => 'tr_xxxxxxxxxxxx',
|
|
'amount' => [
|
|
'value' => '20.00',
|
|
'currency' => 'EUR'
|
|
]
|
|
])
|
|
->embed(RefundCollection::class) // First embedded collection
|
|
->add([
|
|
'resource' => 'refund',
|
|
'id' => 're_12345',
|
|
'amount' => [
|
|
'value' => '10.00',
|
|
'currency' => 'EUR'
|
|
]
|
|
])
|
|
->embed(ChargebackCollection::class) // Second embedded collection
|
|
->add([
|
|
'resource' => 'chargeback',
|
|
'id' => 'chb_12345',
|
|
'amount' => [
|
|
'value' => '20.00',
|
|
'currency' => 'EUR'
|
|
]
|
|
])
|
|
->create()
|
|
]);
|
|
|
|
// Resulting response will contain:
|
|
// - Payment details in main body
|
|
// - Refunds in _embedded.refunds
|
|
// - Chargebacks in _embedded.chargebacks
|
|
```
|
|
|
|
### Error Response Mocking
|
|
Simulate API error responses using dedicated helper methods or the generic error builder:
|
|
|
|
**Common Error Shortcuts**
|
|
```php
|
|
use Mollie\Api\Fake\MockResponse;
|
|
|
|
// 404 Not Found
|
|
$client = MollieApiClient::fake([
|
|
GetPaymentRequest::class => MockResponse::notFound('No payment exists with token tr_xxxxxxxxxxx')
|
|
]);
|
|
|
|
// 422 Validation Error (with optional field reference)
|
|
$client = MollieApiClient::fake([
|
|
CreatePaymentRequest::class => MockResponse::unprocessableEntity(
|
|
detail: 'Amount must be at least €1.00',
|
|
field: 'amount'
|
|
)
|
|
]);
|
|
```
|
|
|
|
**Generic Error Builder**
|
|
```php
|
|
// Custom status code example
|
|
$response = MockResponse::error(
|
|
status: 403,
|
|
title: 'Forbidden',
|
|
detail: 'Insufficient permissions to access this resource'
|
|
);
|
|
|
|
// Special characters handling
|
|
$detail = 'Invalid parameter "recurringType" - did you mean "sequenceType"?';
|
|
$response = MockResponse::unprocessableEntity($detail, 'field');
|
|
```
|
|
|
|
**Error Response Structure**
|
|
All errors follow Mollie's standardized format:
|
|
```json
|
|
{
|
|
"status": 404,
|
|
"title": "Not Found",
|
|
"detail": "No payment exists with token tr_xxxxxxxxxxx",
|
|
"field": "amount" // Only present for validation errors
|
|
}
|
|
```
|