gocanto's avatar
Level 54

Test coverage

Hi Guys, I have a question regarding the phpunit tests coverage tool.

Say, I have this code:

function foo(): void
{ 
    if ($foo === true) {
        return;
    }

    
    throw new Exception('some error');
}

Even though I have a full tests suit that covers the whole function, phpunit marks this file as not completed coverage due to the exception at the end of the function.

I have been told this is due to a hidden return after the exception, but I am not quite sure how to deal with it.

Now, if I move that exception within a else statement, the test coverage is back to normal.

Another work around I found was putting this after the exception call:

//@codeCoverageIgnoreStart
return;
//@codeCoverageIgnoreEnd

Any hint I can follow?

Thanks,

0 likes
8 replies
Punksolid's avatar

Hi @gocanto I don't think that 100% code coverage is that good. Its not that has a hidden return it is that throw Exception will stop everything unless you have a try catch, what you could do are several tests that will trigger all the other flows that isn't the exception, but like in this case you already done it.

1 like
gocanto's avatar
Level 54

@punksolid Thanks for your prompt reply.

It is not about 100% tests coverage, but having cover at all. At the moment, I have all the respective tests in place, but I still get 55% test coverage on a given file due to this.

Hence, proper builds will not approve a PR with less than 80% test coverage.

1 like
Punksolid's avatar

Ok I understand, you could do this but I don't think is a good idea because you will be deactivating code programatically, but only you know where does that happen.

When you are on a test it will skip that. Hope it helps


if (app()->environment('testing')) {
    throw new Exception('some error');
}
impbob36's avatar

Have you tried catching the Exception in phpunit?

    protected function setUp(): void
    {
        parent::setUp();

        \Route::get('/test-exception', function () {
            if (true === false) {
                return true;
            }

            throw new \Exception('some error');
        });
    }

    /**
     * @test
     * */
    public function it_should_throw_an_exception()
    {
     // This will confirm the exception
        $this->expectException(\Exception::class);       
    
     // (Optional) Confirm exception message
        $this->expectExceptionMessage('some error');

        $this->withoutExceptionHandling()  // This is required to stop Laravel intercepting the Exception
            ->get('/test-exception');
    }
1 like
gocanto's avatar
Level 54

The issue is having an exception outside of a controlled structure.

If exceptions are thrown within an if or try catch statements, we would not end up with this case scenario.

gocanto's avatar
Level 54

Note:

It is not about testing whether a givenexception is thrown, but having a full test coverage of a given method/function that throws exceptions outside controlled structures.

Thanks

gocanto's avatar
gocanto
OP
Best Answer
Level 54

I finally found how to deal with it.

  • Do not throw an exception within an unhandled structure.
  • Write a happy path to coverage the code after a given exception is thrown.

Thanks, everybody!

impbob36's avatar

Glad you got it working. Thanks for updating with your solution

Please or to participate in this conversation.