Skip to content

Commit

Permalink
Merge pull request #17 from seregazhuk/add-assertions-for-instances
Browse files Browse the repository at this point in the history
Add assertions for instances
  • Loading branch information
seregazhuk authored Mar 9, 2020
2 parents cb8dbe9 + df1e1e1 commit a56481a
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 31 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Change Log
All notable changes to this project will be documented in this file.

## 0.4.0 - 2020-03-09
### Changed:
- TestCase is now abstract
### Added:
- assertPromiseFulfillsWithInstanceOf() to check class of the resolution value

## 0.3.0 - 2020-03-08
### Updated:
- Dependencies
Expand Down
69 changes: 54 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ assertions for testing ReactPHP promises.
- [Assertions](#assertions)
- [assertPromiseFulfills()](#assertpromisefulfills)
- [assertPromiseFulfillsWith()](#assertpromisefulfillswith)
- [assertPromiseFulfillsWithInstanceOf()](#assertpromisefulfillswithinstanceof)
- [assertPromiseRejects()](#assertpromiserejects())
- [assertPromiseRejectsWith()](#assertpromiserejectswith)

Expand All @@ -33,35 +34,36 @@ The recommended way to install this library is via [Composer](https://getcompose
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.

```
composer require seregazhuk/react-promise-testing
composer require seregazhuk/react-promise-testing --dev
```

## Quick Start
To start using it extend your test classes from `seregazhuk\React\PromiseTesting\TestCase` class,
Just extend your test classes from `seregazhuk\React\PromiseTesting\TestCase` class,
which itself extends PHPUnit `TestCase`:

```php
class MyTest extends TestCase
final class MyTest extends TestCase
{
/** @test */
public function promise_fulfills()
public function promise_fulfills_with_a_response_object()
{
$resolve = function(callable $resolve, callable $reject) {
return $resolve('Promise resolved!');
};

$cancel = function(callable $resolve, callable $reject) {
$reject(new \Exception('Promise cancelled!'));
};

$promise = new Promise($resolve, $cancel);
$this->assertPromiseFulfills($promise);
$browser = new Clue\React\Buzz\Browser($this->eventLoop());
$promise = $browser->get('http://www.google.com/');
$this->assertPromiseFulfillsWithInstanceOf($promise, ResponseInterface::class);
}
}

```

Test above checks that a specified promise fulfills. If the promise rejects this test fails.
Test above checks that a specified promise fulfills with an instance of `ResponseInterface`.

## Event loop

To make promise assertions we need to run the loop. Before each test a new instance of the event loop
is being created (inside `setUp()` method). If you need the loop to build your dependencies you **should**
use `eventLoop()` method to retrieve it.



## Assertions

Expand Down Expand Up @@ -101,6 +103,7 @@ Failed asserting that promise fulfills. Promise was rejected.
```

### assertPromiseFulfillsWith()

`assertPromiseFulfillsWith(PromiseInterface $promise, $value, int $timeout = null): void`

The test fails if the `$promise` doesn't fulfills with a specified `$value`.
Expand Down Expand Up @@ -136,6 +139,42 @@ Failed asserting that promise fulfills with a specified value.
Failed asserting that 1234 matches expected 1.
```

### assertPromiseFulfillsWithInstanceOf()

`assertPromiseFulfillsWithInstanceOf(PromiseInterface $promise, string $class, int $timeout = null): void`

The test fails if the `$promise` doesn't fulfills with an instance of specified `$class`.

You can specify `$timeout` in seconds to wait for promise to be fulfilled.
If the promise was not fulfilled in specified timeout the test fails.
When not specified, timeout is set to 2 seconds.

```php
final class PromiseFulfillsWithInstanceOfTest extends TestCase
{
/** @test */
public function promise_fulfills_with_an_instance_of_class(): void
{
$deferred = new Deferred();
$deferred->resolve(new MyClass);
$this->assertPromiseFulfillsWithInstanceOf($deferred->promise(), MyClass::class);
}
}
```

```bash
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

F 1 / 1 (100%)

Time: 180 ms, Memory: 4.00MB

There was 1 failure:

1) seregazhuk\React\PromiseTesting\tests\PromiseFulfillsWithWithInstanceOfTest::promise_fulfills_with_an_instance_of_class
Failed asserting that promise fulfills with a value of class MyClass.
```

### assertPromiseRejects()
`assertPromiseRejects(PromiseInterface $promise, int $timeout = null): void`

Expand Down
34 changes: 30 additions & 4 deletions src/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use React\EventLoop\LoopInterface;
use React\Promise\PromiseInterface;
use Clue\React\Block;
use Exception;
use React\EventLoop\Factory as LoopFactory;
use React\Promise\Timer\TimeoutException;

class TestCase extends PHPUnitTestCase
abstract class TestCase extends PHPUnitTestCase
{
private const DEFAULT_WAIT_TIMEOUT = 2;

protected $loop;
private $loop;

protected function setUp(): void
{
Expand All @@ -34,7 +35,7 @@ public function assertPromiseFulfills(PromiseInterface $promise, int $timeout =
try {
$this->waitForPromise($promise, $timeout);
} catch (TimeoutException $exception) {
$this->fail($failMessage . 'Promise was rejected by timeout.');
$this->fail($failMessage . 'Promise was cancelled due to timeout.');
} catch (Exception $exception) {
$this->fail($failMessage . 'Promise was rejected.');
}
Expand All @@ -55,14 +56,34 @@ public function assertPromiseFulfillsWith(PromiseInterface $promise, $value, int
try {
$result = $this->waitForPromise($promise, $timeout);
} catch (TimeoutException $exception) {
$this->fail($failMessage . 'Promise was rejected by timeout.');
$this->fail($failMessage . 'Promise was cancelled due to timeout.');
} catch (Exception $exception) {
$this->fail($failMessage . 'Promise was rejected.');
}

$this->assertEquals($value, $result, $failMessage);
}

/**
* @throws AssertionFailedError
*/
public function assertPromiseFulfillsWithInstanceOf(PromiseInterface $promise, string $class, int $timeout = null): void
{
$failMessage = "Failed asserting that promise fulfills with a value of class $class. ";
$result = null;
$this->addToAssertionCount(1);

try {
$result = $this->waitForPromise($promise, $timeout);
} catch (TimeoutException $exception) {
$this->fail($failMessage . 'Promise was cancelled due to timeout.');
} catch (Exception $exception) {
$this->fail($failMessage . 'Promise was rejected.');
}

$this->assertInstanceOf($class, $result, $failMessage);
}

/**
* @param PromiseInterface $promise
* @param int|null $timeout
Expand Down Expand Up @@ -119,4 +140,9 @@ public function waitForPromise(PromiseInterface $promise, int $timeout = null)
{
return Block\await($promise, $this->loop, $timeout ?: self::DEFAULT_WAIT_TIMEOUT);
}

public function eventLoop(): LoopInterface
{
return $this->loop;
}
}
10 changes: 5 additions & 5 deletions tests/PromiseFulfillsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
final class PromiseFulfillsTest extends TestCase
{
/** @test */
public function promise_fulfills()
public function promise_fulfills(): void
{
try {
$deferred = new Deferred();
Expand All @@ -31,21 +31,21 @@ public function it_fails_when_promise_doesnt_fulfill_in_a_specified_timeout(): v
$deferred = new Deferred();

$deferred->reject();
$promise = resolve(3, $this->loop);
$promise = resolve($timeToResolve = 3, $this->eventLoop());

$promise->then(function() use ($deferred){
$promise->then(static function() use ($deferred) {
$deferred->resolve();
});

$this->assertPromiseFulfills($promise, 1);
} catch (Exception $exception) {
$this->assertRegExp(
'/Failed asserting that promise fulfills. Promise was rejected/',
'/Promise was cancelled due to timeout./',
$exception->getMessage()
);

$this->assertRegExp(
'/Promise was rejected by timeout/',
'/Promise was cancelled due to timeout/',
$exception->getMessage()
);
}
Expand Down
85 changes: 85 additions & 0 deletions tests/PromiseFulfillsWithInstanceOfTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace seregazhuk\React\PromiseTesting\tests;

use Exception;
use React\Promise\Deferred;
use function React\Promise\Timer\resolve;
use seregazhuk\React\PromiseTesting\TestCase;

final class PromiseFulfillsWithInstanceOfTest extends TestCase
{
/** @test */
public function promise_fulfills_with_a_value_of_a_specified_class(): void
{
try {
$deferred = new Deferred();
$deferred->resolve(new MyClass());
$this->assertPromiseFulfillsWithInstanceOf($deferred->promise(), MyClass::class, 1);
} catch (Exception $exception) {
$this->assertRegExp(
'/Failed asserting that promise fulfills with a value of class ' . preg_quote(MyClass::class, '/') .'/',
$exception->getMessage()
);

$this->assertRegExp(
'/Failed asserting that .+ matches expected .+/',
$exception->getMessage()
);
}
}

/** @test */
public function it_fails_when_promise_rejects(): void
{
try {
$deferred = new Deferred();

$deferred->reject();
$this->assertPromiseFulfillsWithInstanceOf($deferred->promise(), MyClass::class, 1);
} catch (Exception $exception) {
$this->assertRegExp(
'/Failed asserting that promise fulfills with a value of class ' . preg_quote(MyClass::class, '/') .'/',
$exception->getMessage()
);

$this->assertRegExp(
'/Promise was rejected/',
$exception->getMessage()
);
}
}

/** @test */
public function it_fails_when_promise_doesnt_fulfill_in_a_specified_timeout(): void
{
try {
$deferred = new Deferred();

$deferred->reject();
$promise = resolve($timeToResolve = 3, $this->eventLoop());

$promise->then(
static function() use ($deferred){
$deferred->resolve(new MyClass());
}
);

$this->assertPromiseFulfillsWithInstanceOf($promise, MyClass::class, 1);
} catch (Exception $exception) {
$this->assertRegExp(
'/Failed asserting that promise fulfills with a value of class ' . preg_quote(MyClass::class, '/') .'/',
$exception->getMessage()
);

$this->assertRegExp(
'/Promise was cancelled due to timeout/',
$exception->getMessage()
);
}
}
}

final class MyClass {

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use function React\Promise\Timer\resolve;
use seregazhuk\React\PromiseTesting\TestCase;

final class PromiseResolvesWithTest extends TestCase
final class PromiseFulfillsWithTest extends TestCase
{
/** @test */
public function promise_fulfills_with_a_specified_value(): void
Expand Down Expand Up @@ -58,11 +58,13 @@ public function it_fails_when_promise_doesnt_fulfill_in_a_specified_timeout(): v
$deferred = new Deferred();

$deferred->reject();
$promise = resolve(3, $this->loop);
$promise = resolve($timeToResolve = 3, $this->eventLoop());

$promise->then(function() use ($deferred){
$deferred->resolve();
});
$promise->then(
static function () use ($deferred) {
$deferred->resolve();
}
);

$this->assertPromiseFulfillsWith($promise, 1, 1);
} catch (Exception $exception) {
Expand All @@ -72,7 +74,7 @@ public function it_fails_when_promise_doesnt_fulfill_in_a_specified_timeout(): v
);

$this->assertRegExp(
'/Promise was rejected by timeout/',
'/Promise was cancelled due to timeout/',
$exception->getMessage()
);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/WaitForPromiseToFulfillTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function promise_resolves(): void
$deferred = new Deferred();

$deferred->reject(new Exception());
$value = $this->waitForPromiseToFulfill($deferred->promise());
$this->waitForPromiseToFulfill($deferred->promise());
} catch (Exception $exception) {
$this->assertRegExp(
'/Failed to fulfill a promise. It was rejected with Exception/',
Expand Down

0 comments on commit a56481a

Please sign in to comment.