Lecteur Markdown
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