Code coverage with behat - php

We want to use the most current PHP_CodeCoverage API (https://github.com/sebastianbergmann/php-code-coverage) in our project.
To be sure it's really the case we just made an addtional script in bin/behat-coverage
#!/usr/bin/env php
<?php
require_once (\dirname(__FILE__).'/../vendor/autoload.php');
// Making coverage according to https://github.com/sebastianbergmann/php-code-coverage
$filter = new \SebastianBergmann\CodeCoverage\Filter();
$filter->addDirectoryToWhitelist(\dirname(__FILE__).'/../src');
$filter->removeDirectoryFromWhitelist(\dirname(__FILE__).'/../vendor');
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(new \SebastianBergmann\CodeCoverage\Driver\Xdebug(), $filter);
/**
* Creates the code coverage.
* NOTE! Behat calls the function exit (indirectly). So we have to make the coverage report in a function which
* is fired on exit.
*/
function ____on_behat_shutdown____() {
global $coverage;
$coverage->stop();
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade;
$writer->process($coverage, \dirname(__FILE__).'/../var/code-coverage-report');
}
register_shutdown_function('____on_behat_shutdown____');
$coverage->start('myproj');
require_once(\dirname(__FILE__).'/../vendor/behat/behat/bin/behat');
This seems to work fine.
But:
Is there a way to make execution speed the same as issuing vendor/bin/behat directly?
Is this the best way to use the most current PHP_CodeCoverage API?
Is there a way to make the output colorful again in Git Bash/Power Shell?
Is there a way to custom adjust colors in the shells mentioned above?

Related

Code Coverage with PHPUnit during Runtime

I am using Laravel to build my own website, and I want to know the coverage for my source code during a user interaction event instead of writing testing cases using PHPUnit.
From my perspective, PHPUnit only generate reports that the unit tests touch, but if I want to get the code coverage during the execution, meaning generate the code coverage report after php artisan serve
What should I do to achieve this feature? For instance:
$filter = new Filter;
$filter->includeDirectory('/path/to/directory');
$coverage = new CodeCoverage(
(new Selector)->forLineCoverage($filter),
$filter
);
$coverage->start();
// Some User Interactions to the Web Service
$coverage->stop();
// Generating reports during the interactions
(new HtmlReport)->process($coverage, '/tmp/code-coverage-report');
All comments, answers, and bits of advice are welcome.
Thank you.

How to use the PHP_CodeCoverage library directly?

I'm trying to get the PHP_CodeCoverage library working for the simplest possible case to create an HTML code coverage report, and failing. I have PHP and Xdebug installed. I would rather not specify the versions of those that I am using because I'm hoping there is something simple I am overlooking, but I will provide those details upon request. For now, suffice to say, I am using very new versions of them.
To make this as simple as possible, I distilled it down to 2 files and I still can't get it to do what I want. The 2 files are the automatically generated vendor/autoload.php created by composer when installing the coverage library, and the file that is using the library. This file is at project-root/src/CoverageTest.php. The autoload file is at project-root/vendor/autoload.php.
When I run php src/CoverageTest.php from the terminal, it does generate an HTML report. When I view the report, inside the Code Coverage table, there is only a single row with everything set to "n/a" or "0/0". It does not report any coverage as existing or missing. It doesn't say anything about specific files, classes, functions, or lines. To a large extent the code you see in this test file is the same as what appears on their README page. Here is the code:
<?php
require __DIR__.'/../vendor/autoload.php';
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage;
$coverage->start('<name of test>');
class MathGenius
{
public function add($first, $second)
{
return $first + $second;
}
}
$guru = new MathGenius();
$sum = $guru->add(1, 1);
$coverage->stop();
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade;
$writer->process($coverage, __DIR__.'/../code-coverage-report');
It only reports on whitelisted files and directories. This is how I got the example I posted to work. Before creating the coverage object, I created a filter object, like so:
$filter = new \SebastianBergmann\CodeCoverage\Filter();
Since that example was only attempting to cover itself, I whitelisted that file with the filter like this:
$filter->addFileToWhitelist(__FILE__);
Then when creating the coverage object, you pass in the filter like so:
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(null, $filter);
Here is the full code for the fixed version of the code from the question:
<?php
require __DIR__.'/../vendor/autoload.php';
$filter = new \SebastianBergmann\CodeCoverage\Filter();
$filter->addFileToWhitelist(__FILE__);
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(null, $filter);
$coverage->start('<name of test>');
class MathGenius
{
public function add($first, $second)
{
return $first + $second;
}
}
$guru = new MathGenius();
$sum = $guru->add(1, 1);
$coverage->stop();
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade;
$writer->process($coverage, __DIR__.'/../code-coverage-report');

PHPUnit separation of tests [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm using Symfony 2.8 (latest) for an web-application where every part of the application which could be used alone / reused is an own bundle. For example there is a NewsBundle, GalleryBundle, ContactBundle, AdminBundle (this is a special case - it's only a wrapper-bundle for EasyAdminBundle collecting the traits provided by the specific bundles), UserBundle (Child bundle of FOSUserBundle storing user-entity and templates)
My question is basically, whats the best structure for unit-tests?
Let me explain it a little bit more: In my UserBundle I want to make tests for my implementation of FOSUserBundle. I have a method testing the login-page (via HTTP status-code), login-failure (via error-message), login-successful (via specific code-elements), remember-me (via Cookie), logout (via page-content)
<?php
namespace myNamespace\Admin\UserBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* Class FOSUserBundleIntegrationTest.
*/
class FOSUserBundleIntegrationTest extends WebTestCase
{
/**
* Tests the login, login "remember-me" and logout-functionality.
*/
public function testLoginLogout()
{
// Get client && enable to follow redirects
$client = self::createClient();
$client->followRedirects();
// Request login-page
$crawler = $client->request('GET', '/admin/login');
// Check http status-code, form && input-items
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(1, $crawler->filter('form[action="/admin/login_check"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_username"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_password"]')->count());
$this->assertEquals(1, $crawler->filter('input[type="submit"]')->count());
// Clone client and crawler to have the old one as template
$clientLogin = clone $client;
$crawlerLogin = clone $crawler;
// Get form
$formLogin = $crawlerLogin->selectButton('_submit')->form();
// Set wrong user-data
$formLogin['_username'] = 'test';
$formLogin['_password'] = '123';
// Submit form
$crawlerLoginFailure = $clientLogin->submit($formLogin);
// Check for error-div
$this->assertEquals(1, $crawlerLoginFailure->filter('div[class="alert alert-error"]')->count());
// Set correct user-data
$formLogin['_username'] = 'mmustermann';
$formLogin['_password'] = 'test';
// Submit form
$crawlerLoginSuccess = $client->submit($formLogin);
// Check for specific
$this->assertTrue(strpos($crawlerLoginSuccess->filter('body')->attr('class'), 'easyadmin') !== false ? true : false);
$this->assertEquals(1, $crawlerLoginSuccess->filter('li[class="user user-menu"]:contains("Max Mustermann")')->count());
$this->assertEquals(1, $crawlerLoginSuccess->filter('aside[class="main-sidebar"]')->count());
$this->assertEquals(1, $crawlerLoginSuccess->filter('div[class="content-wrapper"]')->count());
// Clone client from template
$clientRememberMe = clone $client;
$crawlerRememberMe = clone $crawler;
// Get form
$formRememberMe = $crawlerRememberMe->selectButton('_submit')->form();
// Set wrong user-data
$formRememberMe['_username'] = 'mmustermann';
$formRememberMe['_password'] = 'test';
$formRememberMe['_remember_me'] = 'on';
// Submit form
$crawlerRememberMe = $clientRememberMe->submit($formRememberMe);
// Check for cookie
$this->assertTrue($clientRememberMe->getCookieJar()->get('REMEMBERME') != null ? true : false);
// Loop all links on page
foreach ($crawlerRememberMe->filter('a')->links() as $link) {
// Check for logout in uri
if (strrpos($link->getUri(), 'logout') !== false) {
// Set logout-link
$logoutLink = $link;
// Leave loop
break;
}
}
// Reuse client to test logout-link
$logoutCrawler = $clientRememberMe->click($logoutLink);
// Get new client && crawl default-page
$defaultPageClient = self::createClient();
$defaultPageCrawler = $defaultPageClient->request('GET', '/');
// Check http status-code, compare body-content
$this->assertTrue($defaultPageClient->getResponse()->isSuccessful());
$this->assertTrue($logoutCrawler->filter('body')->text() == $defaultPageCrawler->filter('body')->text());
}
}
All this tests will be done in one method because if I would do it in different methods I would have an high amount (5x4 lines = 20 lines copy & paste) of duplicated code. Does this follow the best practice? What is the best practice for separating unit-tests? (or other worded: How would you do it?)
Part two of the question: Is there a possibility to provide helper-functions for test-classes or something working similar to it? I mean methods as example which provide an logged in client. This would be needed for admin function-tests.
Now that your question is more specific, I will provide an answer with some explanation. What you are doing for your first test may work but is not the way you should be testing. It's not so much best practice as it is you circumventing the idea of a unit test, checking assumptions against a single unit of work. Your test has several 'units' of work being tested, and they should all be in separate tests.
Here is a condensed example of more appropriate tests for your first two cases:
public function testLoginForm()
{
$client = self::createClient();
$crawler = $client->request('GET', '/admin/login');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(1, $crawler->filter('form[action="/admin/login_check"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_username"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_password"]')->count());
$this->assertEquals(1, $crawler->filter('input[type="submit"]')->count());
}
public function testLoginFailure()
{
$client = self::createClient();
$crawler = $client->request('GET', '/admin/login');
$form = $crawler->selectButton('_submit')->form();
$form['_username'] = 'test';
$form['_password'] = '123';
$crawler = $client->submit($form);
$this->assertEquals(1, $crawler->filter('div[class="alert alert-error"]')->count());
}
A few things here.
You are worried about code duplication and extra lines of code, but I just created two separate tests that didn't increase the line count at all. I was able to remove the followRedirects() call since it didn't apply to those tests, and I eliminated the two lines of cloning by simply recreating the client and crawler as normal, which is less confusing.
With your code there is only one unit test, but if that test fails it could be for any number of different reasons - login failure, login success, etc. So if that test fails you have to sift through the error messages and find out which part of your system failed. By separating out the tests, when a test fails you know what went wrong simply by the name of the test.
You can eliminate some of your redundant code comments by separating your tests: // Set wrong user-data is no longer needed because the test itself is called testLoginFailure().
Not only is it unit-testing best practice, but there is another caveat when it comes to using WebTestCase, in that you want all of your tests isolated. I've tried to make a static $client variable that the entire class can use, thinking that I'll save memory/time if I only instantiate one instance, but this causes unpredictable behavior when you start running multiple tests. You want your tests to occur in isolation.
You could also use the setUp() and tearDown() functions and have a $this->client and $this->crawler instantiated before each request if you were really trying to eliminate redundant code:
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Crawler;
/*
* #var Client
*/
private $client;
/*
* #var Crawler
*/
private $crawler;
/*
* {#inheritDoc}
*/
protected function setUp()
{
$this->client = self::createClient();
$this->crawler = $this->client->request('GET', '/admin/login');
}
/*
* {#inheritDoc}
*/
protected function tearDown()
{
unset($this->client);
unset($this->crawler);
}
...but then you're creating class-level code to declare those variables, instantiate them, and tear them down. You also ended up adding a lot of additional code, which is what you were trying to avoid in the first place. Additionally, your entire test class is now rigid and inflexible, because you can never request a page other than the login page. Plus, PHPUnit itself states:
The garbage collection of test case objects is not predictable.
The above statement is in regard to if you don't remember to manually clean up your tests. So you may encounter unexpected behavior for those reasons in addition to the other ones I described above.
As for your second question, sure, provide helper-functions or extend the existing *TestCase classes. The Symfony documentation even provides an example for this with a private function that logs in a user. You could put that in an individual test class like their documentation, or you could make your own MyBaseTestCase class that has that function in it.
TL;DR Don't try to be clever with your tests/test cases, separate your tests, and create helper functions or base test case classes to extend from if you reuse a lot of the same setups.

how to run phpunit from code?

I want to use a local installation of PHPUnit (via composer) to run my tests and display it on screen (acessing /admin/tests for instance). But the only way to run tests I found in the documentation was the command line tool.
Bellow is an hypothetical example of what I'm looking for:
$session = new PHPUnit_TestSession('path/to/folder');
$results = $session->runAll();
echo $results->failuresCount();
// other hipotetical $result->methods...
// maybe $results->dump()
This may be an overkill but you are in for a treat: https://github.com/NSinopoli/VisualPHPUnit :)
EDIT Here is a rudimentary use of PHPUnit using the TextUI_TestRunner
// make sure you have PHPUnit on your path
require_once "PHPUnit/Framework/TestSuite.php";
require_once "PHPUnit/TextUI/TestRunner.php";
$suite = new PHPUnit_Framework_TestSuite();
$suite->addTestSuite('YourTestCase');
// run the test suite with TextUI_TestRunner
PHPUnit_TextUI_TestRunner::run($suite);
The YourTestCase class is a subclass of PHPUnit_Framework_TestCase, which you can read more on how to write at the official website: http://www.phpunit.de/manual/3.2/en/writing-tests-for-phpunit.html
However, I'd also recommend getting a copy of this book: http://www.amazon.com/Advanced-PHP-Programming-George-Schlossnagle/dp/0672325616 The author teaches you quite a few cool tricks, including autoloading tests, etc.

integration testing command line PHP

I've got a PHP script that runs at the command line, executing classes that are already unit tested with PHPUnit.
However, I'd like to verify that the script itself has no logical errors and runs properly.
// require classes
require_once 'injectedClass.php';
require_once 'DBClass.php';
require_once 'taskEngine.php';
$injectedObj = new injectedClass;
$dbObj = new DBClass;
$taskRunner = new taskEngine($injectedObj, $dbObj);
$taskRunner->task1()->task2();
$taskRunner->finish();
//etc
Updated Solution
It is as simple as djechelon's answer suggested, I was overthinking it. The solution is to create a PHPUnit test and pre-assign the variables passed into the taskRunner to mock objects. In the live script, a simple check before creating real objects allows the same script to be used for testing and production:
test:
$injectedObj = $this->getMock('injectedClass');
$dbObj = $this->getMock('DBClass');
require_once '/path/to/live/script.php';
$this->assertTrue($taskRunner->finished);
script:
// require classes
if(!isset($injectedObj)) {
$injectedObj = new injectedClass;
}
if(!isset($dbObj)) {
$dbObj = new DBClass;
}
$taskRunner = new taskEngine($injectedObj, $dbObj);
// run tasks
Can't you create a PHPUnit test for your script?
You could perform an integration test by hand, creating a script that runs your script with a set of given input parameters and compare its output to what you could expect.
Beware of the chicken-and-egg problem: your testing script cannot be tested itself by a test bench...
Anyway I'm not sure you need testing your script if it's so simple. A few manual runs might suffice...

Categories