Just some basic helper stuff to help test API endpoints.
- PHP 7.0 and above
- PHPUnit 6.0 and above
- Guzzlehttp 6.2 and above
composer require brunty/api-testcase --dev
Add an environment variable to your PHPUnit Configuration that's your API's base URL:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
<php>
<env name="api_base_url" value="http://httpbin.org"/>
</php>
</phpunit>
Extend the \Brunty\ApiTestCase
class. If you need to configure the client, call $this->configureClientOptions($options);
before calling parent::setUp()
:
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
public function setUp()
{
$options = [
// ...
];
// use this if you want to add additional options to the client when it's constructed
$this->configureClientOptions($options);
parent::setUp();
}
}
The test case uses Guzzle (\GuzzleHttp\Client
) under the surface, so requests are effectively just made through that. If you need to access the client, you can do so with $this->client();
within your test class.
get(string $path [, array $options])
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
/**
* @test
*/
public function the_api_retrieves_all_books()
{
$this->get('/books');
$this->assertResponseOk();
}
}
post(string $path [, array $options])
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
/**
* @test
*/
public function the_api_creates_a_book()
{
$this->post('/books', ['title' => 'My Book']);
$this->assertResponseOk();
}
}
patch(string $path [, array $options])
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
/**
* @test
*/
public function the_api_updates_a_book()
{
$this->patch('/books/1', ['title' => 'My Updated Book']);
$this->assertResponseOk();
}
}
put(string $path [, array $options])
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
/**
* @test
*/
public function the_api_creates_or_updates_a_book()
{
$this->put('/books', ['title' => 'My Updated Book']);
$this->assertResponseOk();
}
}
delete(string $path [, array $options])
<?php
use Brunty\ApiTestCase;
class BooksApiTest extends ApiTestCase
{
/**
* @test
*/
public function the_api_deletes_a_book()
{
$this->delete('/books/1');
$this->assertResponseOk();
}
}
getHeader(string $name)
Returns a response header matching the name.
response()
Returns the response object.
statusCode()
Returns the status code from the response.
rawResponseBody()
Returns the contents of the body of the response.
responseBody($asArray)
Returns the response body, parsed into either an array (if $asArray
is true) or: \stdClass
if the response was JSON, \SimpleXmlElement
if the response was XML.
If the content type of the response cannot be determined to be either XML or JSON, a \Brunty\ContentTypeNotFound
exception will be thrown.
getContentType()
Returns the value of the first Content-Type
header element.
contentTypeIsXml()
Returns true
if the content type is XML, false
otherwise.
contentTypeIsJson()
Returns true
if the content type is JSON, false
otherwise.
The \Brunty\Response
class contains a list of constants for all HTTP status codes - these can help make status code assertions more readable - for example:
$this->assertResponseStatus(\Brunty\Response::HTTP_NO_CONTENT);
as opposed to $this->assertResponseStatus(204);
Assertion | Notes |
---|---|
assertResponseStatus($status) |
|
assertResponseOk() |
(Response code 200) |
assertResponseWasSuccess() |
(200 <= Response Code < 300) |
assertResponseWasRedirect() |
(300 <= Response Code < 400) Note that you may need to set the allow_redirects option to false otherwise status codes of the page after the redirect can be used. |
assertResponseWasClientError() |
(400 <= Response Code < 500) |
assertResponseWasServerError() |
(500 <= Response Code) |
assertResponseWasJson() |
|
assertResponseWasXml() |
|
assertResponseHasKey($key) |
|
assertNodeIsValue($xPathQuery, $value) |
Runs the xpath query against the result (yes, even for JSON - though |
assertRedirectedTo($path) |
Path can be absolute, or relative to the root api_base_url |
This started as a project of boredom one Friday evening, if you find yourself using this, and want more features, please feel free to suggest them, or submit a PR!
Although this project is small, openness and inclusivity are taken seriously. To that end the following code of conduct has been adopted.