Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare v2 #178

Merged
merged 9 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# These are supported funding model platforms

github: hotmeteor
ko_fi: hotmeteor
github: [hotmeteor, bastien-phi, jarrodparkes]
78 changes: 66 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Write tests that verify your API spec doesn't drift from your implementation.
[![Latest Version on Packagist](https://img.shields.io/packagist/vpre/hotmeteor/spectator.svg?style=flat-square)](https://packagist.org/packages/hotmeteor/spectator)
![PHP from Packagist](https://img.shields.io/packagist/php-v/hotmeteor/spectator)

## Requirements

- PHP 8.1
- Laravel 10

## Installation

You can install the package through Composer.
Expand All @@ -26,6 +31,16 @@ php artisan vendor:publish --provider="Spectator\SpectatorServiceProvider"

The config file will be published in `config/spectator.php`.

### Upgrading from v1 to v2

**Important:** Spectator v2 requires PHP 8.1 and Laravel 10. If you are using an older version of PHP or Laravel, you should not upgrade to v2.

While this should typically be a straightforward upgrade, you should be aware of some of the changes that have been made.

Please read the [UPGRADE.md](UPGRADE.md) file for more information.

## Configuration

### Sources

**Sources** are references to where your API spec lives. Depending on the way you or your team works, or where your spec lives, you may want to configure different sources for different environments.
Expand All @@ -34,7 +49,7 @@ As you can see from the config, there's three source types available: `local`, `

---

#### Local Example
#### Local

```env
## Spectator config
Expand All @@ -45,7 +60,7 @@ SPEC_PATH=/spec/reference

---

#### Remote Example
#### Remote

_This is using the raw access link from Github, but any remote source can be specified. The SPEC_URL_PARAMS can be used to append any additional parameters required for the remote url._

Expand All @@ -58,7 +73,7 @@ SPEC_URL_PARAMS="?token=ABEDC3E5AQ3HMUBPPCDTTMDAFPMSM"

---

#### Github Example
#### Github

_This uses the Github Personal Access Token which allows you access to a remote repo containing your contract._

Expand All @@ -76,7 +91,7 @@ SPEC_GITHUB_TOKEN='your personal access token'

---

#### Specifying Your File In Your Tests
### Specifying the Target Spec File

In your tests you will declare the spec file you want to test against:

Expand All @@ -94,11 +109,13 @@ public function testBasicExample()

**Now, on to the good stuff.**

At first, spec testing, or contract testing, may seem counter-intuitive, especially when compared with "feature" or "functional" testing as supported by Laravel's [HTTP Tests](https://laravel.com/docs/8.x/http-tests). While functional tests are ensuring that your request validation, controller behavior, events, responses, etc. all behave the way you expect when people interact with your API, contract tests are ensuring that **requests and responses are spec-compliant**, and that's it.
At first, spec testing, or contract testing, may seem counter-intuitive, especially when compared with "feature" or "functional" testing as supported by Laravel's [HTTP Tests](https://laravel.com/docs/8.x/http-tests).

While _functional_ tests are ensuring that your request validation, controller behavior, events, responses, etc. all behave the way you expect when people interact with your API, _contract_ tests are ensuring that **requests and responses are spec-compliant** - _and that's it_. The data itself could be wrong, but that's outside the scope of a contract test.

### Writing Tests

Spectator adds a few simple tools to the existing Laravel testing toolbox.
Spectator introduces a few simple tools to compliment the existing Laravel testing toolbox.

Here's an example of a typical JSON API test:

Expand Down Expand Up @@ -154,7 +171,12 @@ class ExampleTest extends TestCase

The test is verifying that both the request and the response are valid according to the spec, in this case located in `Api.v1.json`. This type of testing promotes TDD: you can write endpoint contract tests against your endpoints _first_, and then ensure your spec and implementation are aligned.

Within your spec, each possible response should be documented. For example, a single `POST` endpoint may result in a `2xx`, `4xx`, or even `5xx` code response. Additionally, your endpoints will likely have particular parameter validation that needs to be adhered to. This is what makes contract testing different from functional testing: in functional testing, successful and failed responses are tested for outcomes; in contract testing, requests and responses are tested for conformity and outcomes don't matter.
Within your spec, each possible response should be documented. For example, a single `POST` endpoint may result in a `2xx`, `4xx`, or even `5xx` code response. Additionally, your endpoints will likely have particular parameter validation that needs to be adhered to.

This is what makes contract testing different from functional testing:

- in **functional testing**, successful and failed responses are tested for outcomes
- in **contract testing**, requests and responses are tested for conformity and outcomes don't matter.

### Debugging

Expand Down Expand Up @@ -186,6 +208,8 @@ A few custom symbols are used:

## Usage

### Providing a Spec

Define the spec file to test against. This can be defined in your `setUp()` method or in a specific test method.

```php
Expand Down Expand Up @@ -216,6 +240,8 @@ class ExampleTest extends TestCase
}
```

### Testing Requests

When testing endpoints, there are a few new methods:

```php
Expand All @@ -239,7 +265,9 @@ $this
->assertValidResponse();
```

That said, mixing functional and contract testing may become more difficult to manage and read later.
That said, mixing functional and contract testing may become more difficult to manage and read later. It's strongly advised to keep the two types of tests separate.

### Testing Responses

Instead of using the built-in `->assertStatus($status)` method, you may also verify the response that is valid is actually the response you want to check. For example, you may receive a `200` **or** a `202` from a single endpoint, and you want to ensure you're validating the correct response.

Expand Down Expand Up @@ -289,6 +317,33 @@ class ExampleTestCase
}
```

### Deactivating Spectator

If you want to deactivate Spectator for a specific test, you can use the `Spectator::reset` method:

```php
<?php

use Spectator\Spectator;

class ExampleTest extends TestCase
{
public function setUp(): void
{
parent::setUp();

Spectator::using('Api.v1.json');
}

public function testWithoutSpectator()
{
Spectator::reset();

// Run your test without Spectator
}
}
```

## Core Concepts

### Approach
Expand All @@ -304,15 +359,14 @@ For those interested in contributing to Spectator, it is worth familiarizing you

## Sponsors

A huge thanks to all our sponsors who help push Spectator development forward! In particular, we'd like to say a special thank you to our partners:

- Phil Sturgeon ([@philsturgeon](https://github.com/philsturgeon))
A huge thanks to all our sponsors who help push Spectator development forward!

If you'd like to become a sponsor, please [see here for more information](https://github.com/sponsors/hotmeteor). 💪

## Credits

- [Adam Campbell](https://github.com/hotmeteor)
- Created by [Adam Campbell](https://github.com/hotmeteor)
- Maintained by [Bastien Philippe](https://github.com/bastien-phi), [Jarrod Parkes](https://github.com/jarrodparkes), and [Adam Campbell](https://github.com/hotmeteor)
- Inspired by [Laravel OpenAPI](https://github.com/mdwheele/laravel-openapi) package by [Dustin Wheeler](https://github.com/mdwheele)
- [All Contributors](../../contributors)

Expand Down
44 changes: 44 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Upgrading

## From v1 to v2

While this should be a pretty easy upgrade, you should be aware of some of the changes that have been made.

- Minimum PHP version is now 8.1 and minimum Laravel version is now 10.
- The configuration slightly changed. Check the [configuration](config/spectator.php) and update your configuration
accordingly.
- Calling `$response->assertValidRequest()` now fails if the openapi spec is not found or invalid, which was not the
case previously.
- The specification must now be found and valid when calling `$response->assertInvalidRequest()`
or `$response->assertInvalidResponse()`.

Previously, the request or response was considered invalid if the spec was not found or invalid,
thus `$response->assertInvalidRequest()` or `$response->assertInvalidResponse()` did pass.
It will now fail.
- The `$response->assertInvalidRequest()` and `$response->assertInvalidResponse()` previously did not fail if called on
a perfectly valid http call. It will now fail.
- When providing a http code to `$response->assertValidResponse($code)` or `$response->assertInvalidResponse($code)`, we
now prioritize the validation of the actual http code over the validation of the specification.
- Spectator now ignores the charset definition in the `Content-Type` header of the response in order to find the
response definition in the openapi. If you previously defined your openapi with the full `Content-Type` header, you will have to delete the charset part.
```diff
/users:
get:
summary: Get users
tags: []
responses:
'200':
description: OK
content:
- application/json; charset=utf-8:
+ application/json:
schema:
type: object
properties:
id:
type: integer
```
- The validation is stricter than before. Tests that were passing before might now fail. You may need to update your
tests or your openapi specifications.
- A non-empty response body will be now considered invalid against a specification not defining a response content.
- Internal modifications were made. If you extend Spectator, you may need to update your code.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"test": " COLLISION_PRINTER=1 vendor/bin/phpunit --no-output"
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
},
"laravel": {
"providers": [
"Spectator\\SpectatorServiceProvider"
Expand Down