En vous promenant sur Beamreactor, nous stockons votre IP 48h pour des raisons de sécurité.

Lecteur Markdown

testrunner Documentation › TESTRUNNER_DOCUMENTATION

TESTRUNNER_DOCUMENTATION

BeamReactor Test Runner #

Structure #

/plugins/testrunner/
├── testrunner.php		  # Plugin principal
├── testrunner.fr.inc.php   # Locale française
├── tests/				  # Dossier des tests
│   ├── sanitizer.test.php
│   ├── datatypes.test.php
│   ├── sql.test.php
│   └── ...
└── README.md			   # Ce fichier

Utilisation #

Interface Web #

  • Lancer tous les tests : Bouton "Lancer tous les tests"
  • Export JSON : Bouton "Export JSON" (pour CI/CD)
  • Test spécifique : `?obj=testrunner.php&test=sanitizer`

Export JSON (CI/CD) #

# Lancer tous les tests en JSON
curl "https://dev.beamreactor.com/?obj=testrunner.php&test=all&format=json"

# Test spécifique
curl "https://dev.beamreactor.com/?obj=testrunner.php&test=sanitizer&format=json"

Format de réponse JSON:

{
  "summary": {
	"status": "pass",
	"total": 41,
	"passed": 41,
	"failed": 0,
	"errors": 0,
	"skipped": 0,
	"suites": 3,
	"execution_time": 125.5,
	"timestamp": "2025-12-10T15:30:00+01:00"
  },
  "results": {
	"sanitizer": {
	  "name": "Sanitizer",
	  "description": "Tests de validation...",
	  "total": 20,
	  "passed": 20,
	  "failed": 0,
	  "errors": 0,
	  "skipped": 0,
	  "tests": [...]
	}
  }
}

Créer un nouveau test #

1. Créer le fichier #

Créer `/plugins/testrunner/tests/montest.test.php`

2. Structure du fichier #

<?php
/**
 * Ma suite de tests
 */

use App\Sanitizer\Parser;

return [
	'name' => 'Mon Module',
	'description' => 'Tests pour mon module',
	'tests' => [

		// Test simple
		[
			'name' => 'Test basique',
			'callable' => function() {
				// Votre code de test
				$result = 1 + 1;
				return $result === 2; // true = pass, false = fail
			}
		],

		// Test avec setup
		[
			'name' => 'Test avec Parser',
			'callable' => function() {
				$output = Parser::sanitize('test', 'string');
				return $output === 'test';
			}
		],

		// Test qui peut échouer proprement
		[
			'name' => 'Test avec exception',
			'callable' => function() {
				try {
					// Code qui peut throw
					someFunction();
					return true;
				} catch(Exception $e) {
					return false;
				}
			}
		],

		// Test skippé (conditions non remplies)
		[
			'name' => 'Test nécessitant une feature',
			'callable' => function() {
				// Test code
				return true;
			},
			'skip' => true,
			'skip_reason' => 'Feature pas encore implémentée'
		],

		// Test avec message d'erreur custom
		[
			'name' => 'Test avec message',
			'callable' => function() {
				$value = getSomeValue();
				return $value > 10;
			},
			'error_message' => 'La valeur devrait être > 10'
		]
	]
];
?>

3. Statuts possibles #

  • pass : Test réussi (return true)
  • fail : Test échoué (return false)
  • error : Exception levée pendant le test
  • skip : Test ignoré (skip: true)

Bonnes pratiques #

1. Tests indépendants #

Chaque test doit être isolé et ne pas dépendre d'un ordre d'exécution :

// ❌ MAUVAIS - dépend de l'état global
$global_counter = 0;
[
	'name' => 'Test 1',
	'callable' => function() use (&$global_counter) {
		$global_counter++;
		return true;
	}
],
[
	'name' => 'Test 2',
	'callable' => function() use (&$global_counter) {
		return $global_counter === 1; // Dépend du test 1
	}
]

// ✅ BON - tests indépendants
[
	'name' => 'Test 1',
	'callable' => function() {
		$counter = 0;
		$counter++;
		return $counter === 1;
	}
]

2. Assertions claires #

Retourner des booléens clairs :

// ✅ BON
return $output === 'expected';
return strlen($output) === 10;
return is_array($result) && count($result) > 0;

// ❌ MAUVAIS (ambigu)
return $output; // Truthy value, pas explicite

3. Gestion des erreurs #

Catcher les exceptions quand approprié :

[
	'name' => 'Test base de données',
	'callable' => function() {
		global $cfg;

		// Skip si pas de DB
		if(!isset($cfg['dbtable'])) return true;

		try {
			$result = SQL::queryFirst("SELECT...", [], []);
			return is_array($result);
		} catch(Exception $e) {
			// Table n'existe pas = OK pour dev
			return true;
		}
	}
]

4. Tests de performance #

Mesurer le temps d'exécution :

[
	'name' => 'Performance: Large string',
	'callable' => function() {
		$input = str_repeat('a', 100000);

		$start = microtime(true);
		$output = Parser::sanitize($input, 'string');
		$time = (microtime(true) - $start) * 1000; // ms

		// Doit finir en moins de 100ms
		return $time < 100;
	}
]

5. Tests de sécurité #

Vérifier les protections contre XSS, injection, etc. :

[
	'name' => 'Security: XSS protection',
	'callable' => function() {
		$malicious = '<script>alert("XSS")</script>';
		$output = Parser::sanitize($malicious, 'string');
		// Ne doit pas contenir de tag script
		return strpos($output, '<script>') === false;
	}
]

Intégration CI/CD #

GitHub Actions #

name: Tests BeamReactor

on: [push, pull_request]

jobs:
  test:
	runs-on: ubuntu-latest
	steps:
	  - uses: actions/checkout@v2

	  - name: Setup PHP
		uses: shivammathur/setup-php@v2
		with:
		  php-version: '8.1'

	  - name: Run tests
		run: |
		  RESULT=$(curl -s "http://localhost/?obj=testrunner.php&test=all&format=json")
		  STATUS=$(echo $RESULT | jq -r '.summary.status')
		  if [ "$STATUS" != "pass" ]; then
			echo "Tests failed!"
			echo $RESULT | jq '.'
			exit 1
		  fi
		  echo "All tests passed!"

GitLab CI #

test:
  stage: test
  script:
	- apt-get update && apt-get install -y curl jq
	- RESULT=$(curl -s "http://localhost/?obj=testrunner.php&test=all&format=json")
	- STATUS=$(echo $RESULT | jq -r '.summary.status')
	- |
	  if [ "$STATUS" != "pass" ]; then
		echo "Tests failed!"
		echo $RESULT | jq '.'
		exit 1
	  fi
	- echo "All tests passed!"

Sécurité #

Configuration production #

Dans `config.php` :

// Désactiver les tests en production
$cfg['test_mode'] = false;

Niveau d'accès #

Le plugin nécessite `TEST_LEVEL_ADMIN` par défaut. Ajuster dans `testrunner.php` si besoin.

Protection API #

Pour CI/CD avec clé d'API :

// Dans testrunner.php
$api_key = $_GET['key'] ?? '';
if($format === 'json' && $api_key !== $cfg['test_api_key']) {
	http_response_code(403);
	echo json_encode(['error' => 'Invalid API key']);
	exit;
}

Exemples avancés #

Test avec base de données #

[
	'name' => 'DB: Insert and retrieve',
	'callable' => function() {
		global $db_ressource, $cfg['dbtable'];

		$test_data = [
			'username' => 'test_' . uniqid(),
			'email' => 'test@example.com'
		];

		// Insert
		$id = SQL::insert($cfg['dbtable'], $test_data);

		// Retrieve
		$result = SQL::queryFirst(
			"SELECT * FROM $cfg['dbtable'] WHERE id = ?",
			[$id],
			['int']
		);

		// Cleanup
		SQL::delete($cfg['dbtable'], ['id' => $id]);

		return $result['username'] === $test_data['username'];
	}
]

Test avec mock #

[
	'name' => 'Function with mock',
	'callable' => function() {
		// Mock d'une fonction globale
		$original = null;
		if(function_exists('someFunction')) {
			$original = 'someFunction';
		}

		// Créer le mock
		function someFunction() {
			return 'mocked';
		}

		// Test
		$result = myCodeThatUsesSomeFunction();

		// Restore (si possible)
		// ...

		return $result === 'expected';
	}
]

Tips & Tricks #

Debugging #

Ajouter des messages temporaires :

[
	'name' => 'Debug test',
	'callable' => function() {
		$value = getSomeValue();
		// Pour debug, throw avec info
		if($value !== 'expected') {
			throw new Exception("Got: $value, expected: expected");
		}
		return true;
	}
]

Le message apparaîtra dans l'interface.

Grouper les tests #

Créer plusieurs fichiers pour organiser :

  • `sanitizer.test.php` - Tests Sanitizer
  • `datatypes.test.php` - Tests datatypes (IP, email, etc.)
  • `sql.test.php` - Tests SQL::
  • `plugins.test.php` - Tests plugins spécifiques
  • `security.test.php` - Tests de sécurité
  • `performance.test.php` - Tests de performance

Skip conditionnel #

[
	'name' => 'Test nécessitant extension',
	'callable' => function() {
		// Test code
		return true;
	},
	'skip' => !extension_loaded('mbstring'),
	'skip_reason' => 'Extension mbstring requise'
]

Support #

Pour toute question sur le test runner :

  • Email: nowee@beamreactor.com
  • Documentation: https://beamreactor.com/docs
de en fr