Skip to content

Allow Uses* attributes to prevent code coverage #5968

@JonathanGawrych

Description

@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.

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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions