Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  add first implementation
  add cs config
  • Loading branch information
Baptouuuu committed Jul 9, 2023
2 parents 54958cd + 8b37158 commit 744e3d3
Show file tree
Hide file tree
Showing 15 changed files with 736 additions and 85 deletions.
51 changes: 0 additions & 51 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,6 @@ name: CI
on: [push, pull_request]

jobs:
blackbox:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
php-version: ['8.2']
dependency-versions: ['lowest', 'highest']
name: 'BlackBox'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl
coverage: none
- name: Composer
uses: "ramsey/composer-install@v2"
with:
dependency-versions: ${{ matrix.dependencies }}
- name: BlackBox
run: php blackbox.php
blackbox_coverage:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
php-version: ['8.2']
dependency-versions: ['lowest', 'highest']
name: 'BlackBox Coverage'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl
coverage: xdebug
- name: Composer
uses: "ramsey/composer-install@v2"
with:
dependency-versions: ${{ matrix.dependencies }}
- name: BlackBox
run: php blackbox.php
env:
ENABLE_COVERAGE: 'true'
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
psalm:
runs-on: ubuntu-latest
strategy:
Expand Down
5 changes: 5 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

return Innmind\CodingStandard\CodingStandard::config([
'src',
]);
133 changes: 129 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# black-box-symfony

[![Build Status](https://github.com/innmind/black-box-symfony/workflows/CI/badge.svg?branch=master)](https://github.com/innmind/black-box-symfony/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/innmind/black-box-symfony/branch/develop/graph/badge.svg)](https://codecov.io/gh/innmind/black-box-symfony)
[![Build Status](https://github.com/innmind/black-box-symfony/workflows/CI/badge.svg?branch=main)](https://github.com/innmind/black-box-symfony/actions?query=workflow%3ACI)
[![Type Coverage](https://shepherd.dev/github/innmind/black-box-symfony/coverage.svg)](https://shepherd.dev/github/innmind/black-box-symfony)

Description
This package is an extension of [`innmind/black-box`](https://packagist.org/packages/innmind/black-box) to help test [Symfony](https://symfony.com) applications.

## Installation

Expand All @@ -14,4 +13,130 @@ composer require innmind/black-box-symfony

## Usage

Todo
```php
use Innmind\BlackBox\{
Runner\Assert,
Symfony\Application,
};

return static function() {
yield test(
'Login',
function(Assert $assert) {
$app = Application::new($assert); // This assumes the kernel class is 'App\Kernel'
$response = $app
->json()
->post('/login', [
'username' => 'john',
'password' => 'doe',
]);
$response
->statusCode()
->is(200);

$content = $response->body()->json();
$assert
->array($content)
->hasKey('token');
$token = $content['token'];

$app
->get('/me')
->statusCode()
->is(200);
},
);
};
```

### Model Based Testing

By representing the application as a standalone object we can consider it as a system to test and so test it via properties.

```php
use Innmind\BlackBox\{
Set,
Property,
Runner\Assert,
Symfony\Application,
};

/**
* @implements Property<Application>
*/
final class Login implements Property
{
public static function any(): Set
{
return Set\Elements::of(new self);
}

public function applicableTo(object $app): bool
{
return true;
}

public function ensureHeldBy(Assert $assert, object $app): object
{
response = $app
->json()
->post('/login', [
'username' => 'john',
'password' => 'doe',
]);
$response
->statusCode()
->is(200);

$content = $response->body()->json();
$assert
->array($content)
->hasKey('token');
$token = $content['token'];

$app
->get('/me')
->statusCode()
->is(200);

return $app;
}
}
```

And you would run it via:

```php
use Innmind\BlackBox\{
Set,
Properties,
Runner\Assert,
Symfony\Application,
};

return static function() {
yield proof(
'Login',
given(Login::any()),
function(Assert $assert, Login $login) {
$app = Application::new($assert);

$login->ensureHeldBy($assert, $app);
},
);
// and you can even test a sequence of properties to simulate a user actions
yield proof(
'No user interaction should crash the app',
given(Set\Properties::any(
Login::any(),
AnotherProperty::any(),
// etc...
)),
function(Assert $assert, Properties $properties) {
$app = Application::new($assert);

$properties->ensureHeldBy($assert, $app);
},
);
}
```
27 changes: 0 additions & 27 deletions blackbox.php

This file was deleted.

14 changes: 11 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
"issues": "http://github.com/innmind/black-box-symfony/issues"
},
"require": {
"php": "~8.2"
"php": "~8.2",
"innmind/black-box": "~5.0",
"symfony/framework-bundle": "~6.0",
"symfony/browser-kit": "~6.0",
"symfony/http-foundation": "~6.0",
"symfony/http-kernel": "~6.0"
},
"autoload": {
"psr-4": {
Expand All @@ -24,12 +29,15 @@
},
"autoload-dev": {
"psr-4": {
"Tests\\Innmind\\BlackBox\\Symfony\\": "tests/"
"Tests\\Innmind\\BlackBox\\Symfony\\": "tests/",
"App\\": "fixtures/"
}
},
"require-dev": {
"vimeo/psalm": "~5.13",
"innmind/black-box": "~5.0",
"innmind/coding-standard": "~2.0"
},
"provide": {
"innmind/black-box-extension": "5.0"
}
}
12 changes: 12 additions & 0 deletions fixtures/Kernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
declare(strict_types = 1);

namespace App;

use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;

final class Kernel extends BaseKernel
{
use MicroKernelTrait;
}
100 changes: 100 additions & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
declare(strict_types = 1);

namespace Innmind\BlackBox\Symfony;

use Innmind\BlackBox\Runner\Assert;

final class Application
{
private Assert $assert;
private ?Browser $browser = null;

private function __construct(Assert $assert)
{
$this->assert = $assert;
}

public static function new(Assert $assert): self
{
return new self($assert);
}

/**
* @param non-empty-string $uri
*/
public function get(string $uri, array $headers = []): Response
{
return $this->request('GET', $uri);
}

/**
* @param non-empty-string $uri
*/
public function post(string $uri, array $data = [], array $headers = []): Response
{
return $this->request('POST', $uri, $data, [], $headers);
}

/**
* @param non-empty-string $uri
*/
public function put(string $uri, array $data = [], array $headers = []): Response
{
return $this->request('PUT', $uri, $data, [], $headers);
}

/**
* @param non-empty-string $uri
*/
public function delete(string $uri, array $data = [], array $headers = []): Response
{
return $this->request('DELETE', $uri, $data, [], $headers);
}

public function upload(): Upload
{
return Upload::of($this);
}

/**
* @param non-empty-string $method
* @param non-empty-string $uri
*/
public function request(
string $method,
string $uri,
array $post = [],
array $files = [],
array $headers = [],
string $rawBody = '',
): Response {
// Symfony cast all values from this array in the BrowserKit Request to
// simulate that everything goes through as as string via multipart but
// it uses a simple cast and it differs from how a real browser will
// cast booleans, so we do this transformation here
\array_walk_recursive($post, static function(mixed &$value) {
if ($value === true) {
$value = 'true';
}

if ($value === false) {
$value = 'false';
}
});

$client = $this->browser()->client();
$client->request($method, $uri, $post, $files, $headers, $rawBody);

return Response::new(
$this->assert,
$client->getResponse(),
$client->getInternalResponse(),
);
}

private function browser(): Browser
{
return $this->browser ??= Browser::new();
}
}

0 comments on commit 744e3d3

Please sign in to comment.