GitHub Continuous Integration und CodeCoverage mit Travis CI

Für mein öffentliches GitHub Projekt Entity to Rest Bundle für Symfony, habe ich Travis CI, ein Service einer Frima aus Berlin/Friedrichshain, für Continuous Integration benutzt und bin vollbegeistert. Ich musste nur eine .travis.yml erstellen und mich mit meinem Github Account kostenlos anmelden und mit wenigen Handgriffen kann ich jetzt mittels Travis CI:

  • Testen von unterschiedlichen PHP-Versionen
  • Testen von unterschiedlichen Symfony Versionen
  • Markup auf GitHub über den Stand der Tests: Build:success/failed
  • automatische E-Mail Benachrichtigung über das Ergebnis nach jedem Pull-Request

Jetzt erscheint eine schicke Grafik und informiert über den aktuellen Build Status:

travis_github_ci

Das Ergebnis des Builds kann man sich anschauen auf der travis ci Webseite.

Hinweis: Für öffentliche GitHub Repositories ist der Service kostenlos, für private Repositories muss man bezahlen.

Um die CodeCoverage auch anzeigen und berechnen zu können, kann man sich kostenlos bei einem der beiden Anbieter anmelden und muss nur die CodeCoverage Generierung in seiner .travis.yml triggern.

https://codecov.io  Beispiel Projekt

https://coveralls.io Beispiel Projekt

Symfony static laden von Fixtures beim Laden eines Integration Tests in PHPUnit

Mit der statischen Funktion setUpBeforeClass() von PHPunit kann man einmalig vor dem durchführen der Tests  in einer Klasse Fixtures laden und die Datenbank zurücksetzen:

class MyTestCase extends WebTestCase
{
    use FixtureLoadTrait;
 /**
 * @return void
 */
 public static function setUpBeforeClass()
 {

     $fixtures = [
         new TriggerConditionFixture()
     ];
     self::rebuildDataBase($fixtures);
 }

Die Fixtures lassen sich bequem mit dem Trait laden und die Datenbank wird geleert:

<?php

namespace Tests\Integration;

use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Tests\Fixture\BackendBundle\DataFixtures\ORM\LoginFixture;

trait FixtureLoadTrait
{

    /**
     * @param array $fixtures
     *
     * @return void
     */
    protected static function rebuildDataBase(array $fixtures)
    {
        self::bootKernel();
        $fixtureLoader = new ContainerAwareLoader(
            self::$kernel->getContainer()
        );
        foreach($fixtures as $fixture){
            $fixtureLoader->addFixture($fixture);
        }
        $fixtureLoader->addFixture(new LoginFixture());
        self::getFixtureExecutor()->execute($fixtureLoader->getFixtures());
    }

    /**
     * @return ORMExecutor
     */
    protected static function getFixtureExecutor()
    {
        /** @var \Doctrine\ORM\EntityManager $entityManager */
        $entityManager = self::$kernel->getContainer()
            ->get('doctrine')
            ->getManager();

        return new ORMExecutor(
            $entityManager,
            new ORMPurger($entityManager, ['user'])
        );
    }

    /**
     * @return ContainerAwareLoader
     */
    protected static function getFixtureLoader()
    {
        return new ContainerAwareLoader(
            self::$kernel->getContainer()
        );
    }
}

Symfony 3 Test-Datenbank einrichten für Integration Tests

In Symofny wird automatisch beim Ausführen von Tests die  app/config/parameters_test.yml geladen.
Dort sollte dann eine andere Datenbank angegeben werden, der Einfachheit halber mit demselben Datenbank User:

database_host: same_as_dev
database_port: same_as_dev
database_name: test_db
database_user: same_as_dev
database_password: same_as_dev

Dann müssen auf der Konsole folgende Befehle ausgeführt werden:
Cache leeren:
php bin/console cache:clear –env=test

Datenbank erstellen
php bin/console doctrine:database:create –env=test

Tabellen erstellen
hp bin/console doctrine:schema:update –env=test –force

Dann könnne Fixtures geladen werden und Integration Tests geschrieben werden.

 

Testable PHP CSV Streamreader Class inklusive Shunks

class CsvReader implements ReaderInterface
{

    /**
     * shunk size for file import
     */
    private $shunkSize;

    /**
     * @param int $shunkSize
     */
    public function __construct(int $shunkSize)
    {
        $this->shunkSize = $shunkSize;
    }

    /**
     * @param string $file
     * @param callable $callback
     *
     * @return array
     * @throws StorageException
     */
    public function shunkCsvFile(string $file, callable $callback): array
    {
        $file = $this->getFileObject($file);
        $file->setFlags(
            SplFileObject::READ_CSV |
            SplFileObject::SKIP_EMPTY |
            SplFileObject::READ_AHEAD
        );

        $row = 0;
        $data = [];
        if ($file->isFile()) {
            while (!$file->eof()) {
                $line = $file->fgetcsv(';');
                $row++;
                if ($row === 1) {
                    //skip header
                    continue;
                }

                $data[] = $line;

                if (($row % $this->shunkSize) == 0) {
                    $callback($data);
                    $data = [];
                }
            }
        }

        $callback($data);
            
    }

    /**
     * @param string $file
     *
     * @return SplFileObject
     *
     * @codeCoverageIgnore
     */
    protected function getFileObject(string $file): SplFileObject
    {
        return new SplFileObject($file);
    }

Run PHPUnit Unit and Integrations test with different configurations in PHPStorm

If you want to develop unit and integration tests with PHPStorm you can easily bootstrap your application and run your tests. But if you want to develop real unit tests in your local development enviroment without having a database connection or a cache, you need to make sure, that your application is configured differently, when unit tests are running. In this article you will learn how to do that and how to make PHPStorm will automatically recognize which tests/tests suites you want to execute.

Requirement:

Your integration and unit tests are seperated in 2 directories, like:

/tests/unitTests
/tests/integrationsTests

Overview weiterlesen…

PHPUnit @Depends Variablen im @Dataprovider verwenden

Leider ist es zur Zeit nicht möglich für einen PHPUnit Test sowohl einen anderen Test als Vorraussetzung für einen Test zu markieren, damit die Ausführung des Tests im Fehlerfall nicht stattfindet, und gleichzeitig einen Dataprovider für den Test zu verwenden der auf das Ergebnis einen vorherigen Tests zugreift. Das liegt daran, dass Dataprovider vor dem Durchlaufen der Tests initilisiert werden von PHPunit.

Das Problem

class StackTest extends PHPUnit_Framework_TestCase {
    protected static $foo;

    public function provider() { 
        print_r( self::$foo); //does not work
    }

    /**
     * @dataProvider provider
     */
    public function testOne() {
        self::$foo = array();
    }

    /**
     * @depends testOne
     * @dataProvider provider
     */
    public function testTwo( $data ) { 
    }

weiterlesen…