Un point négatif des tests fonctionnels c’est que plus on ajoute des fonctionnalités, plus ils deviennent nombreux et plus ils prennent de temps à être exécutés. C’est un souci pour les développeurs, car il faut attendre qu’ils soient tous corrects pour pousser une branche et également un souci pour l’intégration continue qui ne fait le déploiement qu’une fois les tests vérifiés.

Actuellement Docker est très utilisé, il permet d’avoir facilement un environnement identique à celui de production. Les tests sont donc exécutés dans un container Docker. Sur les machines physiques qui utilisent Docker puis les tests, les goulots d’étranglement sont généralement le CPU et les accès disque. Vu qu’il est facile d’agir sur ceux-ci, c’est le premier élément qui mérite notre attention.

Dans une application Symfony usuelle avec MySQL, lors de tests, les accès disque se situent principalement au niveau du cache applicatif et de l’accès aux données MySQL.

Mettre le cache applicatif en mémoire

Afin d’éviter les accès disque pour la mise en cache de Symfony, il est possible de tout stocker dans des tableaux en mémoire. Cela rend volatile le cache mais ce n’est pas un problème dans le cadre de test.

Exemple de config_test.yml (SF 3.1 only) :

framework:
    annotations:
        cache: app.cache.annotations.array
    serializer:
        cache: app.cache.serializer.array
    validation:
        cache: app.cache.mapping.array
    cache:
        app: app.cache.system.array
        system: app.cache.system.array

services:
  app.cache.system.array:
    class: Symfony\Component\Cache\Adapter\ArrayAdapter
    arguments: ['', '']
    tags:
      - { name: cache.pool, clearer: cache.default_clearer }

  app.cache.annotations.array:
    class: Doctrine\Common\Cache\ArrayCache
    calls:
      - ['setNamespace', ['annotations']]

  app.cache.serializer.array:
    class: Doctrine\Common\Cache\ArrayCache
    calls:
      - ['setNamespace', ['serializer']]

  app.cache.validation.array:
    class: Doctrine\Common\Cache\ArrayCache
    calls:
      - ['setNamespace', ['serializer']]

  app.cache.mapping.array:
    class: Symfony\Component\Validator\Mapping\Cache\DoctrineCache
    arguments: ['@app.cache.validation.array']

Dans la configuration de test, on crée des services supplémentaires qui utilisent les stratégies de cache dans des tableaux PHP. Puis ces services sont utilisés par les éléments de cache du framework.

Les données MySQL en mémoire

Les données de MySQL sont stockées dans des fichiers. Difficile de mettre le tout en mémoire sans faire des images Docker compliquées et spécifiques en fonction des environnements Symfony, ce qui fait peu de sens. Toutefois, Docker propose des fonctionnalités qui permettent de contourner la difficulté.

Dans les images MySQL habituelles, vous êtes amené à partager le dossier data avec votre disque dur, afin de ne pas perdre les données quand le container est supprimé / recréé. Docker dispose d’options dont la possibilité de monter un volume volatile ou tmpfs dans un container. L’idée est donc de surcharger la configuration MySQL pour placer le dossier data dans ce volume volatile quand vous lancez les tests.

Docker vous permet également d’overrider la configuration de base. Ainsi, vous pouvez créer un fichier docker-compose.override.yml qui contient la configuration spécifique pour votre machine par exemple. Dans notre cas nous allons créer un fichier docker-compose.override.yml.test.dist qui contiendra des réglages des différents dossiers de données sur des volumes volatiles.

Exemple de docker-compose.yml :

version: '2'

volumes:
  mysqldata: ~

services:
  mysql:
    image: mysql:5.7.11
    environment:
      - MYSQL_DATABASE=my_database
      - MYSQL_USER=my_user
      - MYSQL_PASSWORD=a_password
      - TZ=Europe/Paris
    volumes:
      - mysqldata:/var/lib/mysql

Exemple de tmpfs.cnf :

[mysqld]
datadir=/tmpfs

Exemple de docker-compose.override.yml.test.dist :

version: '2'

services:
  mysql:
    tmpfs:
      - /tmpfs:size=600M
    volumes:
      - ./docker/mysql/tmpfs.cnf:/etc/mysql/conf.d/tmpfs.cnf:ro

Dans le fichier override, on monte un volume volatile à l’emplacement /tmps en précisant la taille. Ensuite la configuration MySQL est surchargée pour que le dossier des données soit placé sur ce disque.

Conclusion

Voilà, des tests de bout en bout plus rapides. Dans mon cas, j’ai pu constater des gains de temps d’environ 30%.