pestphp / pest

Pest is an elegant PHP testing Framework with a focus on simplicity, meticulously designed to bring back the joy of testing in PHP.
https://pestphp.com
MIT License
9.07k stars 315 forks source link

[Bug]: Some PHP errors fail silently and stop further tests execution #1063

Closed toonvandenbos closed 5 months ago

toonvandenbos commented 5 months ago

What Happened

While developing a new Laravel Package I stumbled across an unexpected "silent" failure in Pest's execution.

In one of my test files, I had a specific test that needed to instantiate a "Fixture" class as such:


use Whitecube\NewPackage\Manager;
use Whitecube\NewPackage\Tests\Fixtures\FakeResolver;

it('can perform this example test', function () {
   $resolver = new FakeResolver();
   $service = new Manager($resolver);

   // ...
});

This Whitecube\NewPackage\Tests\Fixtures\FakeResolver class is implementing an interface my package requires, but at some point I forgot to implement one of its methods. I would expect Pest to tell me about it and show the exception as it does for any other problem encountered during the tests' execution, but instead it does nothing and stops any further testing execution.

To my knowledge, any other exception or error has an appropriate output. For instance, I tried to instantiate an undefined class inside said test, resulting in the expected exception output. It seems to me there only is a weird behavior when an interface's method is not implemented in a concrete class.

How to Reproduce

  1. Create a new "dummy package" directory: mkdir foobar && cd foobar && mkdir src/ && touch src/FooBar.php
  2. Init composer: image
  3. Install and init pestphp/pest as described in the docs: composer require pestphp/pest --dev --with-all-dependencies && ./vendor/bin/pest --init
  4. Create an interface inside the package's src directory:

    namespace Whitecube\Foobar;
    
    interface SampleInterface
    {
        public function methodToImplement();
    }
  5. Create a new fixture class implementing the freshly created interface inside the package's tests/Fixtures directory, but do not implement the interface's methodToImplement method:

    namespace Whitecube\Foobar\Tests\Fixtures;
    
    use Whitecube\Foobar\SampleInterface;
    
    class FakeObject implements SampleInterface
    {
    }
  6. Do not forget to add an autoloading rule in the composer.json file for the classes located inside /tests, then run composer dumpautoload:
    "autoload-dev": {
        "psr-4": {
            "Whitecube\\Foobar\\Tests\\Fixtures\\": "tests/Fixtures"
        }
    },
  7. Write 3 dummy tests and let the middle one create a new instance of Whitecube\Foobar\Tests\Fixtures\FakeObject:

    use Whitecube\Foobar\Tests\Fixtures\FakeObject;
    
    test('sample dummy test', function () {
        expect(true)->toBeTrue();
    });
    
    test('failing dummy test', function () {
        new FakeObject;
        expect(true)->toBeTrue();
    });
    
    test('another dummy test', function () {
        expect(true)->toBeTrue();
    });

Now, run the tests using ./vendor/bin/pest. In my case, nothing is displayed in the output:

image

In order to demonstrate that everything is working as expected, all 3 tests are executed when commenting the new FakeObject; line (or when resolving the issue by implementing the missing method inside FakeObject, of course):

image

For more convenience, I published a sample repository containing the case above.

Sample Repository

https://github.com/whitecube/foobar-pestphp-issue-sample

Pest Version

2.31.0

PHP Version

8.1.14

Operation System

macOS

Notes

No response

MSR2012 commented 5 months ago

I am getting the following error when tried to re-produce.

PHP Fatal error: Class App\Library\FakeObject contains 1 abstract method and must therefore be declared abstract or implement the remaining methods

nunomaduro commented 5 months ago

This is fixed on Pest 2.33 - it will be released tomorrow morning.