phpunit icon indicating copy to clipboard operation
phpunit copied to clipboard

Allow `Uses*` attributes to prevent code coverage

Open JonathanGawrych opened this issue 1 year ago • 2 comments

Right now, the @uses annotation, or the UsesClass or UsesFunction attributes are only useful in the context of Unintentionally Covered Code when running in strict coverage mode.

Our project at work has many legacy layers, and not all those layers are well thought out. It would be very difficult for us to annotate with each test all the parts that a tests covers (we have a controller test, that calls an action, that calls a service, that calls a model, that interacts with events, which call a listener, which calls... etc). In a perfect world we'd test each part in isolation, but with legacy code, it tends to be a lot of work to set up a single layer to test, and it prevents refactoring without a lot of test rework.

This gives us the desire to opt-out parts of the code coverage, rather than opting in. We want the tests to cover the controller, services, and models, but not our middleware, service providers, or other boot up code. We want explicit tests to cover those instead.

Would it be possible to make the @uses/UsesClass/UsesFunction function without @covers/CoversClass/CoversFunction, and disables coverage for those classes/functions?

For example:

class Controller {
    function coverThis() {
        return (new Service())->andCoverThis();
    }
}

class Service {
    function andCoverThis() {
        (new Logger())->butDontCoverThis('service called!');
        return 123;
    }
}

class Logger {
    function butDontCoverThis($message) {
        file_put_contents('logs.txt', $message . PHP_EOL, FILE_APPEND);
    }
}

#[UsesClass(Logger)]
class ControllerTest extends TestCase {
    function testControllerCoverThis() {
        static::assertSame(123, (new Controller())->coverThis());
    }
}

In this case, I want the Controller and Service to be covered, but not my logger. That way I'll see the lack of coverage on Logger, and write specific tests for it. This example may seem trivial (just add Covers instead), but imagine 4 more layers beyond service, each layer calling multiple different things. Excluding the one class is a lot easier and less brittle than including dozens of classes.

JonathanGawrych avatar Sep 26 '24 14:09 JonathanGawrych

Right now, the @uses annotation, or the UsesClass or UsesFunction attributes are only useful in the context of Unintentionally Covered Code when running in strict coverage mode.

Not true anymore: the --uses CLI option can be used to filter tests based on this information.

sebastianbergmann avatar Oct 10 '24 06:10 sebastianbergmann

I didn't know about that feature! That does provide another use for uses, however I'm still trying to figure out how I can avoid marking code as covered. Right now my solution is to use multiple phpunit.xml's with different <source>s then merging the coverage at the end, but maintaining that is kinda gross.

JonathanGawrych avatar Nov 04 '24 17:11 JonathanGawrych