-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
237 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ coverage.xml | |
.temp/coverage.php | ||
*.swp | ||
*.swo | ||
.phpunit.cache/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,55 @@ | ||
# Given When Then (GWT) Plugin for Pest | ||
# PSpec | ||
|
||
> A simple API allows you to structure your tests focused on the behaviour. Given-When-Then separation makes the test easier to understand at a glance. | ||
> PSpec is a Pest plugin for composing multi scenarios tests with a simple API, based on RSpec let. | ||
### Install | ||
```shell | ||
composer require milroyfraser/pest-plugin-gwt --dev | ||
composer require cfx/pspec --dev | ||
``` | ||
|
||
### Usage | ||
### Simple usage | ||
```php | ||
use App\Exceptions\BlockedUserException; | ||
use App\Models\User; | ||
use function Pest\Gwt\scenario; | ||
use function Pest\Laravel\assertDatabaseHas; | ||
|
||
scenario('activate user') | ||
->given(fn() => User::factory()->create()) | ||
->when(fn(User $user) => $user->activate()) | ||
->then(fn(User $user) => assertDatabaseHas('users', [ | ||
'id' => $user->id, | ||
'activated' => true, | ||
])); | ||
|
||
scenario('activate blocked user') | ||
->given(fn() => User::factory()->blocked()->create()) | ||
->when(fn(User $user) => $user->activate()) | ||
->throws(BlockedUserException::class); | ||
``` | ||
use function Cfx\PSpec\context; | ||
use function Cfx\PSpec\expectSubject; | ||
use function Cfx\PSpec\get; | ||
use function Cfx\PSpec\let; | ||
use function Cfx\PSpec\subject; | ||
|
||
[more examples](https://github.com/milroyfraser/pest-plugin-gwt/blob/master/tests/Example.php) | ||
subject(fn () => User::factory()->create(['is_admin' => get('is_admin')])); | ||
|
||
context('when is admin', function () { | ||
let('is_admin', fn() => true); | ||
|
||
**Given** a state | ||
it('returns true', function () { | ||
expectSubject()->is_admin->toBeTrue(); | ||
}); | ||
}); | ||
|
||
Given method accepts a `Closure`. This is where we `Arrange` application state. The return values will become argument/s of the `when` closure. | ||
context('when is not admin', function () { | ||
let('is_admin', fn() => false); | ||
|
||
**When** I do something | ||
it('returns false', function () { | ||
expectSubject()->is_admin->toBeFalse(); | ||
}); | ||
}); | ||
``` | ||
|
||
When method accepts a `Closure`. This is where we `Act` (perform) the operation. The return values will become argument/s of the `then` closure. | ||
### Higher order testing | ||
|
||
**Then** I expect an outcome | ||
```php | ||
use function Cfx\PSpec\context; | ||
use function Cfx\PSpec\getSubject; | ||
use function Cfx\PSpec\let; | ||
|
||
Then method accepts a `Closure`. This is where we `Assert` the outcome. | ||
context('when using high order testing', function () { | ||
let('param2', fn () => 2); | ||
|
||
it('can use high order testing') | ||
->expect(getSubject(...)) | ||
->toEqual(2); | ||
}); | ||
``` | ||
|
||
> If you want to start testing your application with Pest, visit the main **[Pest Repository](https://github.com/pestphp/pest)**. | ||
[more examples](https://github.com/coderfoxbrasil/cfx-pspec/blob/master/tests/Example.php) | ||
|
||
- Explore the docs: **[pestphp.com/docs/plugins/creating-plugins »](https://pestphp.com/docs/plugins/creating-plugins)** | ||
- Follow us on Twitter: **[@pestphp »](https://twitter.com/pestphp)** | ||
- Join us on the Discord Server: **[discord.gg/bMAJv82 »](https://discord.gg/bMAJv82)** | ||
|
||
Pest was created by **[Nuno Maduro](https://twitter.com/enunomaduro)** under the **[Sponsorware license](https://github.com/sponsorware/docs)**. It got open-sourced and is now licensed under the **[MIT license](https://opensource.org/licenses/MIT)**. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cfx\PSpec\Concern; | ||
|
||
use Closure; | ||
|
||
trait HasLetVariables | ||
{ | ||
/** | ||
* @var array<string, array{'resolved': bool, 'value': mixed, 'resolver': Closure}> | ||
*/ | ||
private array $variables = []; | ||
|
||
public function let(string $key, Closure $resolver): self | ||
{ | ||
$this->setVariable($key, $resolver); | ||
|
||
return $this; | ||
} | ||
|
||
public function get(string $key): mixed | ||
{ | ||
return $this->getVariableValue($key); | ||
} | ||
|
||
private function getVariableValue(string $key): mixed | ||
{ | ||
if (! array_key_exists($key, $this->variables)) { | ||
/** @phpstan-ignore-next-line */ | ||
throw new \Exception("Attempt to read $key, when was not set"); | ||
} | ||
|
||
if ($this->variables[$key]['resolved']) { | ||
return $this->variables[$key]['value']; | ||
} | ||
|
||
return $this->resolveVariable($key); | ||
} | ||
|
||
private function setVariable(string $key, Closure $resolver): void | ||
{ | ||
$this->variables[$key] = [ | ||
'resolved' => false, | ||
'resolver' => $resolver, | ||
'value' => null, | ||
]; | ||
} | ||
|
||
private function resolveVariable(string $key): mixed | ||
{ | ||
$value = $this->variables[$key]['resolver'](); | ||
|
||
$this->variables[$key]['value'] = $value; | ||
$this->variables[$key]['resolved'] = true; | ||
|
||
return $value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cfx\PSpec; | ||
|
||
use Cfx\PSpec\Concern\HasLetVariables; | ||
use Closure; | ||
use Pest\PendingCalls\DescribeCall; | ||
|
||
final class SubjectTester | ||
{ | ||
use HasLetVariables; | ||
|
||
private static ?SubjectTester $instance = null; | ||
|
||
public function __construct( | ||
protected Closure $subjectResolver, | ||
) { | ||
self::$instance = $this; | ||
} | ||
|
||
public static function getInstance(): SubjectTester | ||
{ | ||
return self::$instance ?? throw new \Exception('No subject test instance found, did you called subject()'); | ||
} | ||
|
||
public function context(string $context, Closure $tests): DescribeCall | ||
{ | ||
return describe($context, function () use ($tests) { | ||
return $tests(); | ||
}); | ||
} | ||
|
||
public function resolveSubject(): mixed | ||
{ | ||
$subject = $this->subjectResolver; | ||
|
||
return $subject(); | ||
} | ||
} |
Oops, something went wrong.