Mocks, stubs, and spies for PHP.

Overview

Phony

Mocks, stubs, and spies for PHP.

Current version image

Example verification output

Installation

Available as various Composer packages, depending on the test framework in use:

See the section on Integration with test frameworks in the documentation.

Documentation

Full documentation is available.

What is Phony?

Phony is a PHP library for creating various kinds of test doubles, including object mocks, function stubs, and function spies.

Why use Phony?

Support for language features, old and new

Phony is committed to supporting new PHP features as they emerge. Features that require workarounds in other mocking frameworks (such as passed-by-reference arguments), typically "just work" with Phony.

Amongst other features, Phony supports:

Zero-configuration integrations

Integrating Phony with your favorite test framework is as simple as choosing the correct namespace to import. Complimentary features make integrations seamless and intuitive:

Interested in better integration with other test frameworks? So are we! Just open a GitHub issue if there's something we can do.

Refined verification output

Phony has received a great deal of work and refinement where the rubber meets the road; in its verification output:

Example verification output

Phony's output includes succinct but detailed information that is focused on helping you find the cause of the failure as fast as possible. Where appropriate, the output uses difference comparison and color to further highlight important details.

First-class support for functions and callbacks

Phony is designed to provide support for testing both object-based, and function-based systems. In Phony, object mocks are built upon full-featured function level stubs and spies.

This focus on support for procedural programming allows Phony to handle many situations that cannot be handled by solely class-based mocking frameworks. Since Phony's class-level support is based upon its function-level support, the interfaces are consistent, and require very little extra knowledge to use.

Extensive feature set

Phony has an extensive and powerful feature set. For detailed information on a particular feature, select one of the following:

Help

For help with a difficult testing scenario, questions regarding how to use Phony, or to report issues with Phony itself, please open a GitHub issue so that others may benefit from the outcome.

Alternatively, @ezzatron may be contacted directly via Twitter.

Usage

For detailed usage, see the documentation.

Example test suites

See the phony-examples repository.

Standalone usage

Install the eloquent/phony package, then:

use function Eloquent\Phony\mock;

$handle = mock('ClassA');
$handle->methodA->with('argument')->returns('value');

$mock = $handle->get();

assert($mock->methodA('argument') === 'value');
$handle->methodA->calledWith('argument');

Kahlan usage

Install the eloquent/phony-kahlan package, then:

use function Eloquent\Phony\Kahlan\mock;

describe('Phony', function () {
    it('integrates with Kahlan', function () {
        $handle = mock('ClassA');
        $handle->methodA->with('argument')->returns('value');

        $mock = $handle->get();

        expect($mock->methodA('argument'))->toBe('value');
        $handle->methodA->calledWith('argument');
    });
});

The eloquent/phony-kahlan package also provides auto-wired mocks:

use function Eloquent\Phony\Kahlan\on;

describe('Phony for Kahlan', function () {
    it('supports auto-wiring', function (ClassA $mock) {
        $handle = on($mock);
        $handle->methodA->with('argument')->returns('value');

        expect($mock->methodA('argument'))->toBe('value');
        $handle->methodA->calledWith('argument');
    });
});

Peridot usage

Install the eloquent/phony-peridot package, then:

use function Eloquent\Phony\mock;

describe('Phony', function () {
    it('integrates with Peridot', function () {
        $handle = mock('ClassA');
        $handle->methodA->with('argument')->returns('value');

        $mock = $handle->get();

        expect($mock->methodA('argument'))->to->equal('value');
        $handle->methodA->calledWith('argument');
    });
});

The eloquent/phony-peridot package also provides auto-wired mocks:

use function Eloquent\Phony\on;

describe('Phony for Peridot', function () {
    it('supports auto-wiring', function (ClassA $mock) {
        $handle = on($mock);
        $handle->methodA->with('argument')->returns('value');

        expect($mock->methodA('argument'))->to->equal('value');
        $handle->methodA->calledWith('argument');
    });
});

PHPUnit usage

Install the eloquent/phony-phpunit package, then:

use Eloquent\Phony\Phpunit\Phony;

class PhonyTest extends PHPUnit_Framework_TestCase
{
    public function testIntegration()
    {
        $handle = Phony::mock('ClassA');
        $handle->methodA->with('argument')->returns('value');

        $mock = $handle->get();

        $this->assertSame('value', $mock->methodA('argument'));
        $handle->methodA->calledWith('argument');
    }
}

Inception

Please forgive me if this section is opinionated, or if I recall some particular detail of a framework incorrectly. But if you want a TL;DR for why I created Phony, basically:

The first mocking framework I used was probably SimpleTest's. Unfortunately, that's a long time ago now, and I didn't really understand mocking at the time. And to top it off, I can't even remember how SimpleTest's mocks functioned. So let's skip ahead to the first mocking framework I really explored in depth, which was PHPUnit's.

When I first discovered PHPUnit's mocks, they were revolutionary to me. They allowed me to test things really thoroughly, in ways I didn't even know were possible previously. Although PHPUnit's mocking was likely a port of some existing Java mocking system, it was the framework that first opened my eyes to the real potential of test doubles.

Unfortunately it wasn't all sunshine and roses. PHPUnit's mocks were difficult to use, and especially, because of the expect-run-verify style interface, they were difficult to re-use. There was no way to "un-expect" something in a particular test, and when something failed, the true cause of the failure was often difficult to determine.

While searching for a better solution, I stumbled across Phake, which was inspired by Java's extremely popular Mockito mocking framework. Phake was, and still is, an excellent mocking framework. Both Mockito and Phake eschew the expect-run-verify pattern, to great benefit.

By treating stubbing and verification as separate concepts, Phake essentially fixed all of the problems I had with PHPUnit's mocks. Mocks could be re-used easily, and when a test failed, the cause was (usually) clear. I was sold on the evils of expect-run-verify, and swore never to go back.

I believe it was around this time that I heard of Mockery. Although I was fairly happy with Phake, it did have a few little oddities, such as the way it deals with by-reference arguments, and mocking of traits was not possible. So I checked out Mockery, but was immediately put off by its use of expectations; which I felt would have been a huge step backwards.

In fairness, it's possible that Mockery supports other mocking methods, but since the "primary" way it works seems to be based around expect-run-verify, I've never considered it a viable candidate, and have never used it.

At some point around this time I worked on a Node.js project, and explored a handful of the most popular Node mocking frameworks, before settling on the excellent Sinon.JS. Its focus on callback-based stubbing and spying, and its extensive verification API would eventually influence Phony heavily.

It wasn't until HHVM became a viable option in the PHP world that I really had to consider using something other than Phake. I had wanted for a while to start experimenting with HHVM in my projects. Unfortunately Phake had issues that prevented the test suite from even running under HHVM, so it was immediately a problem.

One of my co-workers had intended to work on HHVM support for Phake, but unfortunately it seemed at that time as though work on Phake had stagnated, and it took over a month just to get a PR accepted that added HHVM as a test target. To Phake's credit, HHVM is now supported, and hindsight has taught me that HHVM support is hard.

One project that showed promise was Prophecy, an "opinionated" mocking framework that arose from the phpspec testing framework. While I disliked its use of abstract terms like "prophesize" and "reveal" for method names, the core idea of a separate object instance that can be used to control the actual mock worked really well. So well, in fact, that I would eventually end up using this concept in Phony.

Importantly, Prophecy already supported HHVM, and seemed like a great fit to replace Phake; until I ran into the "opinionated" side of Prophecy's nature. One thing that Prophecy does not support is order verification. For example; verifying that your code opens a file before writing to it. It seemed to me to be a feature whose benefits are self-evident, but the Prophecy developers unfortunately did not agree.

So, fool that I am, I thought "How hard can it be?" and started work on Phony, a mocking framework designed to combine the strengths of its predecessors, and allow testing under HHVM without compromise.

New versions of PHP came along and introduced new language features, and Phony adapted to meet the requirements of testing these features. I was also fortunate enough to be part of a development team at my day job, who were willing to be the test bed for Phony, and it received a lot of real-world usage that contributed immensely to Phony's stability and eventual feature set.

Of course it turned into a much longer journey than I first expected, and Phony continues to be a challenging project to maintain. But for me, it's an invaluable tool that I use almost every day, and I hope it can be the same for you.

Thanks for listening.

Erin (@ezzatron)

Thanks

Special thanks to the following people:

License

For the full copyright and license information, please view the LICENSE file.

Comments
  • Generator and iterable spies change the return value

    Generator and iterable spies change the return value

    I've simplified my testcase to this:

    <?php
    
    namespace Tests\Unit;
    
    use Eloquent\Phony\Phpunit\Phony;
    
    class Consumer
    {
        public function consume()
        {
        }
    }
    
    class Provider
    {
        public function create()
        {
            yield;
        }
    }
    
    class PhonyTest extends \PHPUnit_Framework_TestCase
    {
        public function testPhony()
        {
            $consumer = Phony::mock(Consumer::class);
            $provider = Phony::mock(Provider::class);
            $provider->create->generates();
            $consumer->get()->consume($provider->get()->create());
            self::assertSame(
                $provider->create->firstCall()->returnValue(),
                $consumer->consume->firstCall()->argument(0)
            );
        }
    }
    

    The test above fails with unexpected error:

    Failed asserting that two variables reference the same object.
    

    I've tested that both arguments of the assert are indeed Generators. Just not the same generator which is a bug.

    For confirmation I replaced the line $provider->create->generates(); with $provider->create->returns(new \DateTime); - the test passes with that change.

    documentation 
    opened by enumag 20
  • Check compatibility with PHP 7

    Check compatibility with PHP 7

    • [x] Throwable interface / engine error exceptions
    • [x] Scalar type hints
    • [x] Return type declarations
    • [x] Anonymous classes
    • [x] Closure::call()
    • [x] Expectations
    • [x] Generator return expressions
    enhancement language feature 
    opened by ezzatron 17
  • PHP 8 support

    PHP 8 support

    Hey, if you're here about PHP 8 support, please leave a 🚀.

    I haven't had a lot of time to work on Phony lately, so PHP 8 support is not available yet. Sorry about that. I'm working on PHP 8 support over on the php-8 branch. The first step is to get the existing feature set running under PHP 8, then release a new version ASAP. After that, I'll look into new features to compliment the new stuff in PHP 8.

    Due to the complex nature of this project, I don't really expect any PRs to help out too much, but if you're really keen to contribute, by all means please do.

    Thanks for your patience 🙏

    language feature 
    opened by ezzatron 14
  • Fix fatal error on HHVM with nullable type

    Fix fatal error on HHVM with nullable type

    HHVM does not support the nullable type outside of Hack code.

    Broken by 6bf897cb2215ff209ba5e2794c55faaa63d6b4b4

    Fixes #203 Refs thephpleague/oauth2-client#584

    bug language feature 
    opened by shadowhand 12
  • API improvement discussion

    API improvement discussion

    Currently my biggest issue with Phony is that after

    $handle = Phony::mock(MyClass::class);
    $handle->method->returns('whatever');
    $mock = $handle->get();
    

    PHPStorm and tools like phpstan do not know that $mock instanceof MyClass. Of course I can add @var annotation for PHPStorm and add an ignored error to phpstan but I don't like that. And I don't like having so many variables in the code either.


    Recently I was thinking about some API like this.

    $mock = Helpers::createPhonyMock(
        MyClass::class,
        function (InstanceHandle $handle) {
            $handle->method->returns('whatever');
        }
    );
    

    It is limiting though because InstanceHandle is not easily available outside of the callback and writing the callback is a pain.

    The point of such API is that Helpers::createPhonyMock() always returns an instance of the class that is passed in the first argument. Unlike the first example, this is actually possible to handle in both PHPStorm and phpstan using DynamicReturnTypePlugin and dynamic return type extensions.


    Today I found out there is a method Phony::on() which can return InstanceHandle for existing mock. With this method it might be better to use API like this.

    $mock = Helpers::createPhonyMock(MyClass::class);
    Phony::on($mock)->method->returns('whatever');
    

    It removes the downsides of the second approach while preserving the option of using the dynamic return type tools linked above.


    1. Do you think something like $mock = Phony::createMock($class); should be added to Phony directly?
    2. Do you have some other hidden trick that you use to solve this?
    opened by enumag 11
  • Default

    Default "empty" values for types

    Can I get some input on what would be the best options for default "empty" values for some built-in types? These currently drive Default values for return types, but in future may also drive some auto-wiring of type-hinted function parameters.

    Here's what Phony currently handles:

    | Type | Value | | --- | --- | | (none) | null | | bool | false | | int | 0 | | float | .0 | | string | '' | | array | [] | | stdClass | (object) [] | | callable | function () {} | | Traversable | new EmptyIterator() | | Iterator | new EmptyIterator() | | Generator | (function () {return; yield;})() | | <type> | mock(<type>)->mock() |

    I'm thinking that the EmptyIterator ones are a strange outlier here, in that you can't wrap them in a mock handle and verify their interactions. Perhaps they should be partial mocks? There's also a few in-built classes not catered for.

    Here's some changes I'm considering:

    | Type | Value | | --- | --- | | Traversable | partialMock('EmptyIterator')->mock() | | Iterator | partialMock('EmptyIterator')->mock() | | Exception | partialMock('Exception')->mock() | | RuntimeException etc. | partialMock('RuntimeException')->mock() | | Error | partialMock('Error')->mock() | | AssertionError etc. | partialMock('AssertionError')->mock() | | callable | spy() | | Closure | function () {} |

    enhancement 
    opened by ezzatron 11
  • Seeking suggestions for exporter format improvements

    Seeking suggestions for exporter format improvements

    @jmalloc @koden-km @darianbr @parkertr I'm wondering what you guys think of these exporter format changes I'm thinking of making, and whether you have any other suggestions.

    I've got a feature in the works to add support for limiting output by breadth. Meaning that if an array or object has more than a fixed number of members, the output will be truncated. If I were to follow the current syntax, it would look something like this (with a breadth of 2):

    [1, 2, 3, 4]; // '#0[1, 2, :2]'
    (object) ['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']; // '#0{a: "a", b: "b", :2}'
    

    With :2 indicating that there are 2 members that are truncated. This is similar to when depth limiting kicks in, like so (with a depth of 0):

    [1, 2, 3, 4]; // '#0[:4]'
    (object) ['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']; // '#0{:4}'
    

    First question is; if such a feature were implemented, what would you consider a reasonable default for breadth?

    Secondly, hindsight is making me question why I chose : as the character for this. What do you think about + instead:

    #0[1, 2, +2]
    #0[+4]
    
    #0{a: "a", b: "b", +2}
    #0{+4}
    

    It seems more intuitive to me. The other obvious option is to use ..., but it's a bit less succinct:

    #0[1, 2, ...+2]
    #0[...+4]
    
    #0{a: "a", b: "b", ...+2}
    #0{...+4}
    
    question 
    opened by ezzatron 10
  • Ability to

    Ability to "declare" stubs as functions

    There's probably a good opportunity to replace usage of isolator with a strategy similar to php-mock. That is, declaring functions in a namespace to exploit PHP's function resolution rules, and to swap out real functions with stubbed and spied implementations.

    I'm thinking something like this:

    namespace My\Namespace;
    
    function rollDice()
    {
        return mt_rand(1, 6);
    }
    
    $stub = stub('mt_rand');
    $stub->with(1, 6)->returns(6);
    $stub->declareAs('My\Namespace\mt_rand');
    
    $result = rollDice();
    
    assert(6 === $result);
    $stub->calledWith(1, 6);
    

    In the above example, My\Namespace\mt_rand is declared, but its implementation can later be replaced by any stub that declares itself with the same name. That may take some interesting trickery.

    Thoughts @jmalloc, @darianbr, @koden-km, @parkertr?

    enhancement language feature 
    opened by ezzatron 9
  • Promise stubbing and verification.

    Promise stubbing and verification.

    Based on our discussion the other day, here's some quick and dirty ideas.

    Promise Results

    Easily configure stubs to reject/resolve with a promise. Can be interleaved with regular returns/throws:

    $stub = Phony::stub();
    
    $stub
        ->returns(1)
        ->throws($e1)
        ->resolvesTo(2)
        ->rejectsWith($e2);
    

    The above would be equivalent to:

    $stub
        ->returns(1)
        ->throws($1)
        ->returns(React\Promise\resolve(2))
        ->throws(React\Promise\reject($e));
    

    Cancellation

    Promises can be cancelled, it would be great to be able to verify that the consumer of the promise did/didn't cancel it:

    $stub->cancelled()
    $stub->neverCancelled()
    

    Notifications

    Promises also support notifications, essentially an event handler for a specific promise. It'd be handy to trigger those BEFORE a resolve/reject, but in the same call to the stub.

    Maybe something like:

    $stub->notifies($n1, $n2)->resolvesTo(null)
    

    I don't know if that's possible, but in that example notifies wouldn't specify the behavior for a specific call, but rather configures the values to sent to the promise via $deferred->notify() before resolving with null.

    wontfix integration hard 1.0 blocker 
    opened by jmalloc 9
  • Fatal error: Uncaught Error: Undefined constant

    Fatal error: Uncaught Error: Undefined constant "Eloquent\Phony\Reflection\STDOUT"

    After recently upgrading a Symfony 5.2 project to PHP 8, I'm getting the following error message:

    NOTICE: PHP message: PHP Fatal error:  Uncaught Error: Undefined constant "Eloquent\Phony\Reflection\STDOUT" in /usr/src/app/vendor/eloquent/phony/src/Reflection/FeatureDetector.php:155
    Stack trace:
    #0 /usr/src/app/vendor/eloquent/phony/src/Reflection/FeatureDetector.php(98): Eloquent\Phony\Reflection\FeatureDetector->Eloquent\Phony\Reflection\{closure}(Object(Eloquent\Phony\Reflection\FeatureDetector))
    #1 /usr/src/app/vendor/eloquent/phony/src/Difference/DifferenceEngine.php(50): Eloquent\Phony\Reflection\FeatureDetector->isSupported('stdout.ansi')
    #2 /usr/src/app/vendor/eloquent/phony/src/Difference/DifferenceEngine.php(37): Eloquent\Phony\Difference\DifferenceEngine->setUseColor(NULL)
    #3 /usr/src/app/vendor/eloquent/phony/src/Facade/FacadeContainerTrait.php(193): Eloquent\Phony\Difference\DifferenceEngine->__construct(Object(Eloquent\Phony\Reflection\FeatureDetector))
    #4 /usr/src/app/vendor/eloquent/phony/src/Facade/FacadeContainer.php(18): Eloquent\Phony\Facade\FacadeContainer->initializeContainer(Object(Eloquent\Phony\Assertion\ExceptionAssertionRecorder))
    #5 /usr/src/app/vendor/eloquent/phony/src/initialize.php(10): Eloquent\Phony\Facade\FacadeContainer->__construct()
    #6 /usr/src/app/vendor/composer/autoload_real.php(71): require('/usr/src/app/ve...')
    #7 /usr/src/app/vendor/composer/autoload_real.php(61): composerRequire34151eb540343bf5a399e451787004d2('a4b31be0740d153...', '/usr/src/app/ve...')
    #8 /usr/src/app/vendor/autoload.php(7): ComposerAutoloaderInit34151eb540343bf5a399e451787004d2::getLoader()
    #9 /usr/src/app/public/index.php(10): require('/usr/src/app/ve...')
    #10 {main}
      thrown in /usr/src/app/vendor/eloquent/phony/src/Reflection/FeatureDetector.php on line 155
    

    One solution could be explicitly checking for the CLI SAPI:

    --- a/vendor/eloquent/phony/src/Reflection/FeatureDetector.php
    +++ b/vendor/eloquent/phony/src/Reflection/FeatureDetector.php
    @@ -152,7 +152,7 @@ class FeatureDetector
                     }
                     // @codeCoverageIgnoreEnd
    
    -                return function_exists('posix_isatty') && @posix_isatty(STDOUT);
    +                return function_exists('posix_isatty') && PHP_SAPI === 'cli' && @posix_isatty(STDOUT);
                 },
             ];
         }
    
    opened by yanneg 5
  • Final class as return type

    Final class as return type

    I expected this to work but it throws Eloquent\Phony\Mock\Exception\FinalClassException:

    <?php
    
    final class FinalClass {}
    
    interface SomeInterface
    {
        public function someMethod(): FinalClass;
    }
    
    $final = new FinalClass;
    
    $handle = mock(SomeInterface::class);
    
    $handle->someMethod->returns($final);
    

    In fact it fails as soon as $handle->someMethod is called. Is it the expected behavior? I assumed it would be ok as I specify the instance to return. What is the workaround for this?

    bug 
    opened by pmall 5
  • PHP 8.1 compatibility (WIP)

    PHP 8.1 compatibility (WIP)

    Handles https://github.com/eloquent/phony/issues/255, the never return type and some more issues that showed up as errors when running tests.

    There are still some failing tests under PHP 8.1 left to be handled.

    opened by keksa 0
  • PHP 8.1 internal method return type deprecation notices

    PHP 8.1 internal method return type deprecation notices

    I've got a bunch of deprecation notices when mocking exceptions, or classes implementing Countable, IteratorAggregate or ArrayAccess.

    Return type of PhonyMock_ConnectionException_89::__wakeup() should either be compatible with Exception::__wakeup(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_TemplateRenderException_90::__wakeup() should either be compatible with Exception::__wakeup(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Exception_93::__wakeup() should either be compatible with Exception::__wakeup(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Collection_119::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Collection_119::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Collection_119::offsetExists($a0) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Collection_119::offsetGet($a0) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    Return type of PhonyMock_Collection_119::offsetSet($a0, $a1) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
    

    I've managed to track the cause to this RFC - https://wiki.php.net/rfc/internal_method_return_types

    The return types will be fully in the reflection in PHP 9, but PHP 8.1 only includes them as "tentative return types" and the FunctionSignatureInspector::signature() method can't handle it.

    For example the \Exception::__wakeup() $functionString looks like this:

    Method [  public method __wakeup ] {
    
      - Parameters [0] {
      }
      - Tentative return [ void ]
    }
    

    The tentative return type is not handled by the FunctionSignatureInspector::RETURN_PATTERN regex, so the code for mock class is generated without return type and the deprecation notice gets triggered.

    If I understand the RFC correctly, this will stop being a problem in PHP 9.0 as the reflection will just show it as straight up return type, but PHP 9.0 is still ways ahead.

    opened by keksa 3
  • Cannot provide ad-hoc implementation of static function that returns self type.

    Cannot provide ad-hoc implementation of static function that returns self type.

    Given:

    abstract class C {
        abstract static function fun(): self;
    }
    
    Phony::partialMock(
        [
            C::class,
            [
                'static fun' => function () {
                    return new C();
                },
            ]
        ]
    );
    

    The following fatal error occurs:

    Fatal error: Declaration of PhonyMock_C_8::fun() must be compatible with C::fun(): C in vendor/eloquent/phony/src/Mock/MockFactory.php(91) : eval()'d code on line 5
    

    This makes some sense, as that closure is obviously not returning the self type, however adding the self type doesn't/can't work in this scenario either, as in most cases it would refer to the type of the surrounding class (such as a PHPUnit test suite implementation, for example).

    FWIW, this is the error given when naively adding :self to the closure:

    Call to undefined method ReflectionFunction::getDeclaringClass()
    

    Which I assume is simply because it's a closure, it does not have a declaring class (ReflectionFunction, not ReflectionMethod).

    As a workaround I've implemented this static function by hand-rolling a class in my test suite, but this had a number of undesirable knock-on effects, including preventing verifications using onStatic() from working for this function.

    My PHP is pretty rusty at this point so I'm having trouble coming up with any good solutions, but figured I may as well document this issue before I forget the details.

    opened by jmalloc 0
  • Support for PHP 8 named arguments

    Support for PHP 8 named arguments

    Following on from #250.

    Issues to consider:

    • Constructor argument names
    • Tests for argument name clashes
    • Expanding with() et. al. to support named arguments?
    • Magic calls added by traits
    language feature 
    opened by ezzatron 0
Releases(5.0.2)
  • 5.0.2(Feb 17, 2021)

  • 5.0.1(Feb 8, 2021)

    • [FIXED] Functions and methods with "unavailable" default values no longer cause exceptions to be thrown when attempting to determine the default value.
    Source code(tar.gz)
    Source code(zip)
  • 5.0.0(Dec 21, 2020)

    • [BC BREAK] PHP 7.2 is no longer supported.
    • [NEW] Added support for PHP 8 union types (#250).
    • [NEW] Added support for PHP 8 false types (#250).
    • [NEW] Added support for PHP 8 mixed types (#250).
    • [NEW] Added support for PHP 8 static return types (#250).
    • [NEW] Added support for parent types (#250).

    Known issues

    • PHP 8 named arguments are not supported.
    • No guarantees are made about which specific value will be returned from a stub (or mock method) with a union return type, when no explicit return value is specified. The value may differ depending on the order in which the union type's members appear.
    Source code(tar.gz)
    Source code(zip)
  • 4.0.1(Aug 29, 2020)

  • 4.0.0(Dec 31, 2019)

    • [BC BREAK] PHP 7.1 is no longer supported.
    • [NEW] Added support for specifying ad-hoc mock property types (#247).
    • [IMPROVED] Improved exporter output for array references (#244).
    • [IMPROVED] Improved exporter output for weak references (#245).
    • [IMPROVED] Improved type hints and type declarations for better integration with PHPStan.
    Source code(tar.gz)
    Source code(zip)
  • 3.2.0(Oct 30, 2019)

  • 3.1.0(Dec 6, 2018)

  • 3.0.1(Jun 7, 2018)

    • [FIXED] Fixed a bug where creating handles for uncallable methods with final class return types would throw exceptions (#237 - thanks @pmall).
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Mar 13, 2018)

    See the migration guide for detailed upgrade information.

    • [BC BREAK] PHP 7.0 is no longer supported (#233).
    • [BC BREAK] An explicit argument is now required in setUseColor().
    • [BC BREAK] The handle->clazz() method was renamed to handle->class().
    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(Oct 5, 2017)

  • 2.0.0(Sep 29, 2017)

    See the migration guide for detailed upgrade information.

    • [BC BREAK] PHP 5 is no longer supported (#216).
    • [BC BREAK] HHVM is no longer supported (#216, #219).
    • [BC BREAK] Removed inOrderSequence, checkInOrderSequence, anyOrderSequence, and checkAnyOrderSequence from the facade (#215).
    • [BC BREAK] Stubs created outside of a mock now have their "self" value set to the stub itself, instead of the stubbed callback (#226).
    • [NEW] Implemented anInstanceOf() (#220).
    • [NEW] Implemented emptyValue() (#218).
    • [IMPROVED] Support for PHP 7.2 features, including the object typehint (#224).
    • [IMPROVED] Improved the error message produced when a default return value cannot be produced, because the return type is a final class (#228).
    • [IMPROVED] Reduced the amount of output generated when mocks, stubs, and spies are encountered by var_dump() (#223).
    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Jul 4, 2017)

  • 1.0.0(Apr 24, 2017)

    • [BC BREAK] Third-party test framework integrations have been moved to separate Composer packages (#216).
    • [BC BREAK] Dropped support for Counterpart, Mockery, Phake, and Prophecy matchers (#216).

    Migrating to 1.x

    No code changes should be required, but in some cases, the Composer package name and version constraint will need to be updated:

    • If you're using Phony under PHPUnit:
      • Change the Composer package from eloquent/phony to eloquent/phony-phpunit.
      • Pick the appropriate version constraint for the version of PHPUnit you intend to use. See the eloquent/phony-phpunit repository for more information.
    • If you're using Phony under Peridot:
      • If you were using eloquent/peridot-phony, change the Composer package to eloquent/phony-peridot.
      • If you were using eloquent/phony, no changes are required, but consider trying eloquent/phony-peridot for the added auto-wired mock support. See the eloquent/phony-peridot repository for more information.
    • If you're using Phony under SimpleTest, change the Composer package from eloquent/phony to eloquent/phony-simpletest.
    • If you're using Phony under Pho, change the Composer package from eloquent/phony to eloquent/phony-pho.
    • For other frameworks, or standalone use, no changes are required.
    Source code(tar.gz)
    Source code(zip)
  • 0.14.7(Apr 22, 2017)

  • 0.14.6(Jan 3, 2017)

  • 0.14.5(Dec 7, 2016)

    • [FIXED] Mock handle substitution fixed for threw() and receivedException() verifications (#211).
    • [IMPROVED] Inline exporter now uses tilde (~) to indicate truncated content (#210).
    • [IMPROVED] Refactored the feature detector.
    Source code(tar.gz)
    Source code(zip)
  • 0.14.4(Nov 23, 2016)

  • 0.14.3(Nov 13, 2016)

  • 0.14.2(Nov 10, 2016)

  • 0.14.1(Nov 4, 2016)

  • 0.14.0(Sep 30, 2016)

  • 0.13.5(Sep 20, 2016)

    • [IMPROVED] Support for PHP 7.1 iterable pseudo-type (#195).
    • [IMPROVED] Support for PHP 7.1 void pseudo-type (#195).
    • [IMPROVED] Support for PHP 7.1 nullable types (#195).
    • [IMPROVED] Disallow ASCII delete in symbol names, matching the change in PHP 7.1 (#195).
    Source code(tar.gz)
    Source code(zip)
  • 0.13.4(Aug 17, 2016)

  • 0.13.3(Aug 7, 2016)

  • 0.13.2(Aug 4, 2016)

  • 0.13.1(Aug 2, 2016)

  • 0.13.0(Jul 15, 2016)

    • [BC BREAK] Renamed $handle->mock() to $handle->get() (#180).
    • [BC BREAK] Removed verify() and verifyStatic() (#179).
    • [BC BREAK] Removed magic calls from mock handles. All stubbing must now use with(), and all call argument verification must now use calledWith() (#179).
    • [IMPROVED] Improved exporting of mock handles, stubs, spies, and closures (#177).
    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(Jul 13, 2016)

    • [BC BREAK] Replaced the term "traversable" with "iterable". Any function or method with "traversable" in the name has also been renamed accordingly (#164).
    • [BC BREAK] Stubs now return empty values by default, instead of forwarding (#174).
    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Jul 12, 2016)

  • 0.10.2(Jul 6, 2016)

    • [IMPROVED] Complete overhaul of verification output, with improvements to the output of all verifications (#161, #170).
    • [IMPROVED] Improved exporting of closures under HHVM (#166).
    • [FIXED] Fixed calledOn() behavior (#160).
    • [FIXED] Fixed verification output under Windows (#167).
    • [FIXED] Error reporting is now correctly restored in all cases (#168).
    • [FIXED] Fixed the recording of static magic method calls (#169).
    Source code(tar.gz)
    Source code(zip)
Infrastructure and testing helpers for creating CQRS and event sourced applications.

Broadway is a project providing infrastructure and testing helpers for creating CQRS and event sourced applications. Broadway tries hard to not get in your way.

null 1.5k Dec 30, 2022
Removes final keywords from source code on-the-fly and allows mocking of final methods and classes

Removes final keywords from source code on-the-fly and allows mocking of final methods and classes. It can be used together with any test tool such as PHPUnit or Mockery.

David Grudl 326 Dec 9, 2022
The modern, simple and intuitive PHP unit testing framework.

atoum PHP version atoum version 5.3 -> 5.6 1.x -> 3.x 7.2 -> 8.x 4.x (current) A simple, modern and intuitive unit testing framework for PHP! Just lik

atoum 1.4k Nov 29, 2022
:heavy_check_mark: PHP Test Framework for Freedom, Truth, and Justice

Kahlan is a full-featured Unit & BDD test framework a la RSpec/JSpec which uses a describe-it syntax and moves testing in PHP one step forward. Kahlan

Kahlan 1.1k Jan 2, 2023
A PHP Module, that help with geneting of task script for playwright and send it node.js

A PHP Module, that help with geneting of task script for playwright and send it node.js

LuKa 13 Dec 7, 2022
Library that provides collection, processing, and rendering functionality for PHP code coverage information.

phpunit/php-code-coverage Provides collection, processing, and rendering functionality for PHP code coverage information. Installation You can add thi

Sebastian Bergmann 8.5k Jan 5, 2023
A PHP library for mocking date and time in tests

ClockMock Slope s.r.l. ClockMock provides a way for mocking the current timestamp used by PHP for \DateTime(Immutable) objects and date/time related f

Slope 44 Dec 7, 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
SimpleTest is a framework for unit testing, web site testing and mock objects for PHP

SimpleTest SimpleTest is a framework for unit testing, web site testing and mock objects for PHP. Installation Downloads All downloads are stored on G

SimpleTest 147 Jun 20, 2022
Analyses and produces a report with testability issues of a php codebase

Analyses and produces a report with testability issues of a php codebase

Edson Medina 134 Oct 27, 2022
An effort to make testing PHP code as easy and fun as its JavaScript equivalent

An effort to make testing PHP code as easy and fun as its JavaScript equivalent when using the excellent Jasmine, from which syntax and general usage is shamelessly borrowed.

Johan Stenqvist 24 Apr 22, 2022
PHP client for Selenium/WebDriver protocol. Previously facebook/php-webdriver

Php-webdriver library is PHP language binding for Selenium WebDriver, which allows you to control web browsers from PHP.

php-webdriver 4.7k Jan 3, 2023
The most powerful and flexible mocking framework for PHPUnit / Codeception.

AspectMock AspectMock is not an ordinary PHP mocking framework. With the power of Aspect Oriented programming and the awesome Go-AOP library, AspectMo

Codeception Testing Framework 777 Dec 12, 2022
Some shorthand functions for skipping and focusing tests.

Pest Plugin: Shorthands This repository contains the Pest Plugin Shorthands. If you want to start testing your application with Pest, visit the main P

Thomas Le Duc 10 Jun 24, 2022
The Phoronix Test Suite is the most comprehensive testing and benchmarking platform

The Phoronix Test Suite is the most comprehensive testing and benchmarking platform available for Linux, Solaris, macOS, Windows, and BSD operating systems.

Phoronix Test Suite 1.9k Jan 7, 2023
CommandHelper - is a very useful thing for quick code testing and more!

CommandHelper CommandHelper - is a very useful thing for quick code testing and more! Examples: Code: require_once('commandhelper.php');

RuvSleep 1 Feb 11, 2022
PHPStan PHPUnit extensions and rules

PHPStan PHPUnit extensions and rules PHPStan PHPUnit This extension provides following features: createMock(), getMockForAbstractClass() and getMockFr

PHPStan 359 Dec 28, 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