ReactPHP Promises Testing
A library that provides a set of convenient assertions for testing ReactPHP promises. Under the hood uses clue/php-block-react to block promises.
When testing asynchronous code and promises things can be a bit tricky. This library provides a set of convenient assertions for testing ReactPHP promises.
Table of Contents
Installation
Dependencies
Library requires PHP 7.3.0 or above.
The recommended way to install this library is via Composer. New to Composer?
See also the CHANGELOG for details about version upgrades.
composer require seregazhuk/react-promise-testing --dev
Quick Start
Use the trait seregazhuk\React\PromiseTesting\AssertsPromise
or extend your test classes from seregazhuk\React\PromiseTesting\TestCase
class, which itself extends PHPUnit TestCase
.
final class MyTest extends TestCase
{
/** @test */
public function promise_fulfills_with_a_response_object()
{
$browser = new Clue\React\Buzz\Browser($this->eventLoop());
$promise = $browser->get('http://www.google.com/');
$this->assertPromiseFulfillsWithInstanceOf($promise, ResponseInterface::class);
}
}
Using the trait:
use PHPUnit\Framework\TestCase;
use seregazhuk\React\PromiseTesting\AssertsPromise;
final class MyTest extends TestCase
{
use AssertsPromise;
/** @test */
public function promise_fulfills_with_a_response_object()
{
$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 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
assertPromiseFulfills()
public function assertPromiseFulfills(PromiseInterface $promise, int $timeout = null): void
The test fails if the $promise
rejects.
You can specify $timeout
in seconds to wait for promise to be resolved. If the promise was not fulfilled in specified timeout the test fails. When not specified, timeout is set to 2 seconds.
final class PromiseFulfillsTest extends TestCase
{
/** @test */
public function promise_fulfills(): void
{
$deferred = new Deferred();
$deferred->reject();
$this->assertPromiseFulfills($deferred->promise(), 1);
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 189 ms, Memory: 4.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\PromiseFulfillTest::promise_fulfills
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
.
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.
final class PromiseFulfillsWithTest extends TestCase
{
/** @test */
public function promise_fulfills_with_a_specified_value(): void
{
$deferred = new Deferred();
$deferred->resolve(1234);
$this->assertPromiseFulfillsWith($deferred->promise(), 1);
}
}
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\PromiseFulfillsWithTest::promise_fulfills_with_a_specified_value
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.
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);
}
}
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
The test fails if the $promise
fulfills.
You can specify $timeout
in seconds to wait for promise to be resolved. If the promise was not fulfilled in specified timeout, it rejects with React\Promise\Timer\TimeoutException
. When not specified, timeout is set to 2 seconds.
final class PromiseRejectsTest extends TestCase
{
/** @test */
public function promise_rejects(): void
{
$deferred = new Deferred();
$deferred->resolve();
$this->assertPromiseRejects($deferred->promise());
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 175 ms, Memory: 4.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\PromiseRejectsTest::promise_rejects
Failed asserting that promise rejects. Promise was fulfilled.
assertPromiseRejectsWith()
assertPromiseRejectsWith(PromiseInterface $promise, string $reasonExceptionClass, int $timeout = null): void
The test fails if the $promise
doesn't reject with a specified exception class.
You can specify $timeout
in seconds to wait for promise to be resolved. If the promise was not fulfilled in specified timeout, it rejects with React\Promise\Timer\TimeoutException
. When not specified, timeout is set to 2 seconds.
final class PromiseRejectsWithTest extends TestCase
{
/** @test */
public function promise_rejects_with_a_specified_reason(): void
{
$deferred = new Deferred();
$deferred->reject(new \LogicException());
$this->assertPromiseRejectsWith($deferred->promise(), \InvalidArgumentException::class);
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 136 ms, Memory: 4.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\PromiseRejectsWithTest::promise_rejects_with_a_specified_reason
Failed asserting that promise rejects with a specified reason.
Failed asserting that LogicException Object (...) is an instance of class "InvalidArgumentException".
assertTrueAboutPromise()
assertTrueAboutPromise(PromiseInterface $promise, callable $predicate, int $timeout = null): void
The test fails if the value encapsulated in the Promise does not conform to an arbitrary predicate.
You can specify $timeout
in seconds to wait for promise to be resolved. If the promise was not fulfilled in specified timeout, it rejects with React\Promise\Timer\TimeoutException
. When not specified, timeout is set to 2 seconds.
final class AssertTrueAboutPromiseTest extends TestCase
{
/** @test */
public function promise_encapsulates_integer(): void
{
$deferred = new Deferred();
$deferred->resolve(23);
$this->assertTrueAboutPromise($deferred->promise(), function ($val) {
return is_object($val);
});
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 136 ms, Memory: 4.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\AssertTrueAboutPromiseTest::promise_encapsulates_integer
Failed asserting that false is true.
assertFalseAboutPromise()
assertFalseAboutPromise(PromiseInterface $promise, callable $predicate, int $timeout = null): void
The test fails if the value encapsulated in the Promise conforms to an arbitrary predicate.
You can specify $timeout
in seconds to wait for promise to be resolved. If the promise was not fulfilled in specified timeout, it rejects with React\Promise\Timer\TimeoutException
. When not specified, timeout is set to 2 seconds.
final class AssertFalseAboutPromiseTest extends TestCase
{
/** @test */
public function promise_encapsulates_object(): void
{
$deferred = new Deferred();
$deferred->resolve(23);
$this->assertFalseAboutPromise($deferred->promise(), function ($val) {
return is_int($val);
});
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 136 ms, Memory: 4.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\AssertFalseAboutPromiseTest::promise_encapsulates_object
Failed asserting that true is false.
Helpers
waitForPromiseToFulfill()
function waitForPromiseToFulfill(PromiseInterface $promise, int $timeout = null)
.
This helper can be used when you want to resolve a promise and get the resolution value.
Tries to resolve a $promise
in a specified $timeout
seconds and returns resolved value. If $timeout
is not set uses 2 seconds by default. The test fails if the $promise
doesn't fulfill.
final class WaitForPromiseToFulfillTest extends TestCase
{
/** @test */
public function promise_fulfills(): void
{
$deferred = new Deferred();
$deferred->reject(new \Exception());
$value = $this->waitForPromiseToFulfill($deferred->promise());
}
}
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 223 ms, Memory: 6.00MB
There was 1 failure:
1) seregazhuk\React\PromiseTesting\tests\WaitForPromiseToFulfillTest::promise_fulfills
Failed to fulfill a promise. It was rejected with Exception.
waitForPromise()
function waitForPromise(PromiseInterface $promise, int $timeout = null)
.
Tries to resolve a specified $promise
in a specified $timeout
seconds. If $timeout
is not set uses 2 seconds by default. If the promise fulfills returns a resolution value, otherwise throws an exception. If the promise rejects throws the rejection reason, if the promise doesn't fulfill in a specified $timeout
throws React\Promise\Timer\TimeoutException
.
This helper can be useful when you need to get the value from the fulfilled promise in a synchronous way:
$value = $this->waitForPromise($cache->get('key'));