fpcunit/fr

From Lazarus wiki
Jump to navigationJump to search

English (en) français (fr) polski (pl)

Vue d'ensemble

fpcunit est un framework de test d'unité à DUnit/JUnit/SUnit. Il vous permet d'écrire rapidement un ensemble de test pour une unité (logique) de code (pas nécessairement la même chose qu'une unité Pascal, bien que cela soit souvent le cas).

Les méthodologies de développement comme la conception dirigée par les tests (Test Driven Design, TDD) utilise ceci pour garantir que vos attentes/spécifications dans votre unité en premier, ensuite écrivez votre code principal, puis exécutez vos tests et améliorez le code jusqu'à ce que tous les tests passent.

Non seulement fpcunit vous permet dinspecter visuellement vos exécutions de tests, vous pouvez aussi collecter les résultats systématiquement (en utilisant la sortie XML), et l'utiliser pour comparer les versions pour p.ex. les tests de non-régression (i.e. vous exécutez vos tests de non-régression en utilisant la sortie de l'unité de test).

Capture d'écran de l'IHM du test runner :

guitestrunner.png

L'image montre que sur 10 tests exécutés, 6 tests ont échoué. Les exceptions EAssertionFailure indiquent que les assertions de test (voir en dessous) ne sont pas satisfaites i.e. le test a échoué. Les messages associés indiquent le résultat que le test attendait et le résultat obtenu.

Emploi dans FPC/Lazarus

Les tests FPCUnit sont utilisés dans le framework de test de base de données : Databases/fr#Ex.C3.A9cution_de_tests_de_bases_de_donn.C3.A9es_de_FPC.

Il y a aussi des tests pour le compilateur FPC/les paquet du coeur (core package), mais ceux-ci sont probablement antérieurs à la fpcunit et utilisent une approche plus simple.

Emploi

It's easiest to use Lazarus to set up a new test project for you. Below are some descriptions of which procedures/methods to use for what purpose.

Méthode Setup

This procedure is present in all FPCUnit tests. It sets up the test environment before each test is run - in other words not only before and after the complete test suite is run, but for every test. You can use this to e.g. fill a database with test data.

Méthode Teardown

This procedure is present in all FPCUnit tests and is the reverse of Setup. It cleans up the test environment after each test is run. You can use this to e.g. clear test data from a database.

Décorateur de test : OneTimeSetup et OneTimeTearDown

The Setup and Teardown procedures mentioned above are run once per test. You can also run Setup and Teardown procedures once per instance/execution of your test run.

To do this, use OneTimeSetup and OneTimeTearDown and inherit from the TTestSetup "test decorator" and register it, e.g.:

uses
...
testdecorator
...
  TDBBasicsTestSetup = class(TTestSetup)
    protected
      procedure OneTimeSetup; override;
      procedure OneTimeTearDown; override;
    end; 
...
initialization
// make sure you register your test along with the decorator so it knows to run the setup/teardowns
  RegisterTestDecorator(TDBBasicsTestSetup, TTestDBBasics);

Tests

You write your own tests as published procedures in the test class (private, protected or public will not work). You can use AssertEquals etc to specify what should be tested, and give a suitable message when the test fails.

If you want to fail a test, you can e.g. use

if 5=0 then //ridiculous example but you understand what I mean. You can use other Assert* procedures to test equality much easier etc.
  AssertTrue('This part of the code should never have been reached in this test.',false);

Note: there must be a better way of doing this...

If the test fails, an EAssertionFailedError will be raised with the message you specify in Assert*. This way, you can add a series of subtests and tell which subtest failed. Note: the test runner will stop at the first assertion failure, so subsequent subtests will not be performed. If you do want to always test everything, split out these subtests in separate test procedures.

Instead of the Assert* procedures, you can also use the DUnit compatible Check* procedures (e.g. CheckEquals) which give more descriptive error messages in the test results: they include expected and actual values.

The order in which the tests are run are the order in which they appear in the test class definition.

Exemple de test

  Ttestexport1 = class(Ttestcase)
...
  published
    procedure TestOutput;
...
procedure Ttestexport1.TestOutput;
const
  OutputFilename='output.csv';
begin
  TestDataSet.Close;

  if FileExists(OutputFilename) then DeleteFile(OutputFileName);
  TestDataset.FileName:=OutputFileName;
  TestDataset.Open;
  // Fill test data
  TestDataset.Append;
  TestDataset.FieldByName('ID').AsInteger := 1;
  // Data with quotes
  TestDataset.FieldByName('NAME').AsString := 'J"T"';
  TestDataset.FieldByName('BIRTHDAY').AsDateTime := ScanDateTime('yyyymmdd', '19761231', 1);
  TestDataset.Post;

  TestDataset.Last;
  TestDataset.First;
  TestDataset.First;
  AssertEquals('Number of records in test dataset', 1, TestDataset.RecordCount);
  TestDataset.Close;
end;

Hiérarchie de tests

In simple cases, you (or Lazarus) would register all your test cases with calls like:

uses 
...
testregistry
...
initialization
  RegisterTest(Ttestexport1); //you pass the class name to register it for running

However, you can also create multiple layers to group your test cases if your project gets big:

initialization
  RegisterTest('WorldDominationApp.ExportCheesePlan',Ttestexport1); //The levels are separated by periods 
  RegisterTest('WorldDominationApp.Obsolete',TtestPinkysBrain1); //another category

Sortie

The console test runner can output in XML (either original FPCUnit format, or a more advanced DUnit2-like format if you use the xmltestreport unit), plain text and latex (e.g. usable for PDF export) formats. The GUI test runner outputs to XML if needed (using the same xmltestreport XML format).

Personnalisation de la sortie

You can use your own "listener" that listens to the test results and outputs the test data in whatever way you want. Create a T*Listener that implements the ITestListener interface. It's only 5 required methods to implement.

In your test runner application (e.g. a copy of fpctestconsole), add a listener object; register that test listener with the testing framework (e.g. your console test runner) via the TestResult.AddListener() call, and it will be fed test results as they happen.

Testdbwriter

An example of a custom listener is the database output writer available at https://bitbucket.org/reiniero/testdbwriter. This writer will save all test results to a database, which is optimized for receiving large amounts of test results (handy for using on CI server like Jenkins or for importing/consolidating test results). The mentioned repository contains an example that runs the db test framework test results to (another) database.

A faire : adapter ceci ; utiliser la nouvelle unité xml

An example of an available extra listener is TXMLResultsWriter in the xmlreporter unit in <fpc>\packages\fcl-fpcunit\src\xmlreporter.pas.

todo: actually, dbtestframework seems to use the old xml output method... An example of an adapted test runner that uses an extra listener can be found in <fpc>\packages\fcl-db\tests\dbtestframework.pas, which contains this code to output to custom listeners (an XML writer and a digest writer that stuffs the output in a .tar archive, handy to process remotely):

uses
 ...the rest of the units needed for test runners...
 fpcunit,...
 // the units with TXMLResultsWriter and TDigestResultsWriter
 testreport,DigestTestReport
...
Procedure LegacyOutput;

var
  FXMLResultsWriter: TXMLResultsWriter;
  FDigestResultsWriter: TDigestResultsWriter;
  testResult: TTestResult;

begin
  testResult := TTestResult.Create;
  FXMLResultsWriter := TXMLResultsWriter.Create;
  FDigestResultsWriter := TDigestResultsWriter.Create(nil);
  try
    testResult.AddListener(FXMLResultsWriter);
    testResult.AddListener(FDigestResultsWriter);
    // Set some properties specific for this results writer:
    FDigestResultsWriter.Comment:=dbtype;
    FDigestResultsWriter.Category:='DB';
    FDigestResultsWriter.RelSrcDir:='fcl-db';
    //WriteHeader is specific for this listener; it writes the header to an XML file
    //notice that it is not called for FDigestResultsWriter
    FXMLResultsWriter.WriteHeader;
    // This performs the actual test run, and the output will be processed by the listeners:
    GetTestRegistry.Run(testResult);
    // Likewise WriteResult is specific for this listener; it writes 
    FXMLResultsWriter.WriteResult(testResult);
  finally
    testResult.Free;
    FXMLResultsWriter.Free;
    FDigestResultsWriter.Free;
  end;
end;

Alternatives

  • DUnit2 - a huge improvement over the original DUnit. Originally written for Delphi only, and which is used by the huge test suite of the tiOPF framework.
  • FPTest - a fork of DUnit2, and which is tuned specifically for use with the Free Pascal Compiler.

Lazarus

Lazarus has the consoletestrunner and GUI test runner units, which can be installed by installing the FPCUnitTestRunner package. This will help you create and run your unit tests using a GUI (or console, if you want to).

The consoletestrunner is compatible with FPC so you don't need Lazarus to compile it. The Lazarus version is slightly different to the one in FPC (e.g. use of UTF8 output etc).

The GUI runner is easier to use.

In the GUI runner, if you want to run all tests, currently you first need to click on a test element before the Run all tests button is activated.

GDB bug/fonctionnalité

Note (September 2012): a bug/undocumented feature in the debugger used by Lazarus/FPC (gdb) means that passing --all as run parameters has no effect. Passing this parameter can be useful when debugging console fpcunit test runners has no effect. Workaround: use -a. See bug [1]

Voir aussi