PHPUnit assertions for testing ReactPHP promises

Overview

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.

Build Status Maintainability Test Coverage Total Downloads

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'));
Comments
  • Add AssertPromise trait

    Add AssertPromise trait

    Sometimes you can't or don't want to extend a class in your test class. This makes it possible to use the package without the requirement of extending TestCase.

    opened by ttrig 3
  • How to implement with WampServer (Ratchet) ?

    How to implement with WampServer (Ratchet) ?

    Hello.

    Sergey, how to implement this PHPUnit assertions for ReactPHP with https://github.com/ratchetphp/Ratchet/blob/master/src/Ratchet/Wamp/WampServer.php ?

    Thx. :)

    help wanted question 
    opened by andreybolonin 2
  • v0.6.1 requires PHP >=8.0 but documentation states

    v0.6.1 requires PHP >=8.0 but documentation states "Library requires PHP 7.3.0 or above"

    Does the library really require 8.0 now?

    I've been waiting for a new release that would update its dependency to phpunit 9.x (which also requires 7.3) but we don't yet support PHP 8.x so we're caught between two stools now!

    opened by mdissington 1
  • assertPromiseRejectsWith is wrong

    assertPromiseRejectsWith is wrong

    Hi Sergey, I think this method is wrong: https://github.com/seregazhuk/php-react-promise-testing/blob/f82a634375866486fe2cbcf255634ba4867b00f5/src/AssertsPromise.php#L102

    I't should look like this

     /**
         * @throws AssertionFailedError
         */
        public function assertPromiseRejectsWith(PromiseInterface $promise, string $reasonExceptionClass, int $timeout = null): void
        {
            try {
                $this->waitForPromise($promise, $timeout);
            } catch (Exception $reason) {
                $this->assertInstanceOf(
                    $reasonExceptionClass, $reason, 'Failed asserting that promise rejects with a specified reason.'
                );
                return;
            }
    
            $this->fail('Failed asserting that promise rejects. Promise was fulfilled.');
        }
    

    You reject your promise with "LogicException" and that is catched here: https://github.com/seregazhuk/php-react-promise-testing/blob/f82a634375866486fe2cbcf255634ba4867b00f5/src/AssertsPromise.php#L106

    That will actually create assertion like this:

    $this->assertInstanceOf(\InvalidArgumentException::class, new \LogicException());
    

    And this will not pass because InvalidArgumentException extends LogicException but not vice versa. Trying this as new test will fail with:

    Failed asserting that LogicException Object (...) is an instance of class "InvalidArgumentException".
    

    Finally your test will catch this as PHPUnit\Framework\ExpectationFailedException and assert this exception for valid messages and this is why your test passes.

    I think that test should look like this:

    /** @test */
        public function promise_rejects_with_a_specified_reason(): void
        {
            $deferred = new Deferred();
            $deferred->reject(new \InvalidArgumentException());
            $this->assertPromiseRejectsWith($deferred->promise(), \LogicException::class);
        }
    

    This should reject promise with "InvalidArgumentException" which is actually instance of "LogicException::class"

    opened by zkabic 1
  • Add assertTrueAboutPromise() and assertFalseAboutPromise()

    Add assertTrueAboutPromise() and assertFalseAboutPromise()

    Dear maintainer,

    I kindly request that you add the assertTrueAboutPromise() and assertFalseAboutPromise() test assertions to the library as I believe they are helpful in performing a vastness of checks on values subsumed in promises.

    opened by ace411 0
Releases(v0.6.1)
Owner
Sergey Zhuk
Sergey Zhuk
Async HTTP proxy connector, tunnel any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP.

clue/reactphp-http-proxy Async HTTP proxy connector, tunnel any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP.

Christian Lück 43 Dec 25, 2022
Simple, async SOAP webservice client, built on top of ReactPHP.

clue/reactphp-soap Simple, async SOAP web service client library, built on top of ReactPHP. Most notably, SOAP is often used for invoking Remote proce

Christian Lück 62 Jul 5, 2022
Integration testing helpers for the Slim Framework

Slim Test Helpers Integration testing helpers for the Slim Framework 3 For a full example, please see the companion repo at there4/slim-unit-testing-e

There4 60 Oct 26, 2022
Additional PHPUnit assertions and helper functions

Jasny PHPUnit extension Additional functionality for PHPUnit. Callback mock - assert that callback is called with correct arguments. Safe mocks - disa

Arnold Daniels 2 Jul 24, 2022
Few additional testing assertions for Laravel views

Laravel View Test Assertions Few additional assertions for testing Laravel views. Why Laravel has well established and documented way of testing reque

null 13 Jun 12, 2022
A set of helpful assertions when testing Laravel applications.

Installation composer require amirrezam75/laravel-assertions I was working on a project and in order to test oauth2 redirection, I ended up with somet

Amir Reza Mehrbakhsh 2 Sep 23, 2022
Provides generic data providers for use with phpunit/phpunit.

data-provider Installation Run composer require --dev ergebnis/data-provider Usage This package provides the following generic data providers: Ergebni

null 25 Jan 2, 2023
Qase-phpunit - Qase TMS PHPUnit reporter.

Qase TMS PHPUnit reporter Publish results simple and easy. How to integrate composer require qase/phpunit-reporter Example of usage The PHPUnit report

Qase TMS 6 Nov 24, 2022
Mockery - Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library

Mockery Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its c

Mockery 10.3k Jan 1, 2023
Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests

PHPUnit Polyfills Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests. Requirements Instal

Yoast 147 Dec 12, 2022
Promises/A implementation for PHP.

Promise A lightweight implementation of CommonJS Promises/A for PHP. The master branch contains the code for the upcoming 3.0 release. For the code of

ReactPHP 2.2k Jan 3, 2023
Promises/A+ library for PHP with synchronous support

Guzzle Promises Promises/A+ implementation that handles promise chaining and resolution iteratively, allowing for "infinite" promise chaining while ke

Guzzle 7.3k Jan 3, 2023
A minimalistic implementation of Promises for PHP

libPromise A minimalistic implementation of Promises for PHP. Installation via DEVirion Install the DEVirion plugin and start your server. This will c

null 8 Sep 27, 2022
:computer: Parallel testing for PHPUnit

ParaTest The objective of ParaTest is to support parallel testing in PHPUnit. Provided you have well-written PHPUnit tests, you can drop paratest in y

null 2k Dec 31, 2022
PHP libraries that makes Selenium WebDriver + PHPUnit functional testing easy and robust

Steward: easy and robust testing with Selenium WebDriver + PHPUnit Steward is a set of libraries made to simplify writing and running robust functiona

LMC s.r.o. 219 Dec 17, 2022
PHPUnit extension for database interaction testing.

This extension is no longer maintained DbUnit PHPUnit extension for database interaction testing. Installation Composer If you use Composer to manage

Sebastian Bergmann 224 Aug 20, 2022
Mock implementation of the Translation package, for testing with PHPUnit

PoP Translation - Mock Mock implementation of the Translation package, for testing with PHPUnit Install Via Composer composer require getpop/translati

PoP 1 Jan 13, 2022
The objective of ParaTest is to support parallel testing in PHPUnit

The objective of ParaTest is to support parallel testing in PHPUnit. Provided you have well-written PHPUnit tests, you can drop paratest in your project and start using it with no additional bootstrap or configurations!

null 2k Dec 31, 2022
Learn unit testing with PHPUnit.

PHPUnit Exercise Running PHPUnit ./vendor/bin/phpunit # with filter which tests to run ./vendor/bin/phpunit --filter <pattern> Running Pint ./vendor/

Nopal 2 Aug 23, 2022