Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code coverage when not testing protected/private methods with PHPUnit

I know it's possible to test private/protected methods with PHPUnit using reflection or other workarounds.

But most sources tell me that it's not best practice to write tests for private methods inside of a class.

You are supposed to test the class as if it were a "black box" — you just test for expected behavior by comparing inputs with outputs disregarding the internal mechanics. Writing tests for classes should also notify you to unused private methods, by showing lack of code coverage.

When I test my class and generate an HTML report, it shows the private methods as not covered by tests, even though the lines from which they are called are absolutely executed/covered. I know that the private methods are executed, because if they weren't the assertions on my class would not pass.

Is this expected behavior in PHPUnit? Can I strive for 100% coverage, while still testing private methods only indirectly?

Some simplified example code (using RestBundle in Symfony2):

class ApiController extends FOSRestController {

/*
 * @REST\View()
 * @REST\Get("/api/{codes}")
 */
public function getCodesAction($codes) {
    $view = new View();
    $view->setHeader('Access-Control-Allow-Origin', '*');
    $view->setData(array('type' => 'codes','data' => $this->_stringToArray($codes)));
    $view->setFormat('json')->setHeader('Content-Type', 'application/json');
    return $this->handleView($view);
}

private function _stringToArray($string){
    return explode('+',$string);
}

The public function shows as "covered", the private function is indirectly covered but shows colored red in PHPUnit reports.

Test:

class ApiControllerTest extends WebTestCase {

    public function test_getCodesAction(){
        $client = static::createClient();
        $client->request('GET', '/api/1+2+3');
        $this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
    }

}

This is just a silly example of course, I could just as well include the explode() right there in the public function; But the controllers I'm writing tests for contain much more intricate and re-usable private functions which transform data in more complex ways (but are still side-effect free).

like image 273
okdewit Avatar asked Jul 23 '15 12:07

okdewit


People also ask

What is code coverage in PHPUnit?

In computer science, code coverage is a measure used to describe the degree to which the source code of a program is tested by a particular test suite. A program with high code coverage has been more thoroughly tested and has a lower chance of containing software bugs than a program with low code coverage.

Does code coverage include private methods?

Code coverage checks for private methods too. Generally, there are 2 scenarios where private method gets called. For this case, you will need to call the public method in your tests and cover the underlying private method directly and satisfying the condition from your tests.

Should we unit test protected methods?

If you are aiming high code coverage (I suggest you should), you should test your all methods regardless of they are private or protected. Protected is a kind of different discussion point, but in summary, it should not be there at all.


1 Answers

In Phpunit you can specify the Covered Methods with special annotation, as descrived in the doc.

You can do something like this:

    class ApiControllerTest extends WebTestCase {

        /**
         * @covers ApiController::getCodesAction
         * @covers ApiController::_stringToArray
         */
        public function test_getCodesAction(){
            $client = static::createClient();
            $client->request('GET', '/api/1+2+3');
            $this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
        }

    }

Hope this help

like image 195
Matteo Avatar answered Sep 22 '22 15:09

Matteo