Vincent Snijders pravi: > Hi, > > Suppose I have a component TMyDataProcessor with the following declaration: > > type > TMyDataProcessor = class > function process(const s: string) : string; > end; > > Now I want to test it with different strings, but instead of hard coding > them I put them in a inifile, so I can easily extend the test. > > [test 1] > Input=Test > Output=tEST > > [test 2] > Input=1234 > Output=1234 > > Now I write a fpcunit test that reads those tests input/output pairs > from the ini file and executes them, see following (psuedo) code: > > procedure TMyTestCase.TestOutputs; > var > MyDataProcessor: TMyDataProcessor; > begin > for each section in the ini-file do begin > MyDataProcessor := TMyDataProcessor.Create; > Load Input, Load Output > AssertEquals(SectionName + 'failed. ', > Output, MyDataProcessor(Input)); > MyDataProcessor.Free; > end; > end; > > As far as I can see, the drawback of using this method is that if there > is a failure in test 1, test 2 won't run at all. I will loose > information about what could have been wrong. > > A solution would be to have different test methods for each test in the > ini file, but I want to avoid that, because then adding a test, will > mean that I need to change the code (after having created the data). > > Do you know how I can create data driven tests, so that it will display > the test results of the second test, even if the first test fails. > > Vincent
Yes, there is an elegant solution. Joost has already asked me the same question two months ago and I've prepared in that occasion a small example of parameterized testcase that can be used for data driven tests. The file with a short comment is attached. HTH, Dean
unit paramstests; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fpcunit, testutils, testregistry; type {sometimes the different tests to run have a lot of common functionality and they differ only for the presence of a different parameter, or a different property of the class under test. To remove duplication and unnecessary code there is the possibility to manually build a parameterized test suite, see the following example that can be used as a template. The steps are simple, create a new constructor for the testcase that has the parameters you'll want to pass in the tests (in the following examples the parameters are a string input parameter and an integer parameter to use for the testing of the results). In this constructor you have to call the inherited CreateWithName constructor passing the name of the actual test (in the following code the actual test is the DoTest procedure that performs a simple test of the lenght function for the string passed as parameter to the testcase). The parameters are then stored in some private fileds of the testcase. Then you'll have to construct the test suite manually, see the Suite class function, passing the constructor of the testcase with the wanted parameters. Finally you'll add the constructed test to the test registry using the GetTestRegistry.AddTest function in the initialization section. In this example you'll see that four isolated different tests with the same name (DoTest) will be created. The only drawback is in the fact that in case of failure it's not immediate to see which test went wrong, as the tests have the same name. I suggest to pass some information in the string description of the extended assertequals function as a hint (in the DoTest example below I've inserted the parameter that was passed into the test) The same parameterized testcase could be constructed by loading the parameters from an xml file or from an ini file instead of storing them in the private fields of the testcase. I leave this simple implementation as an exercise for the reader :) { TParameterizedTestCase } TParameterizedTestCase = class(TTestCase) private aParam: string; aOut: integer; public constructor Create(const aParameter: string; aOutput: integer); virtual; overload; class function Suite: TTestSuite; published procedure DoTest; end; implementation { TParameterizedTestCase } constructor TParameterizedTestCase.Create(const aParameter: string; aOutput: integer); begin //create a new DoTest testcase inherited CreateWithName('DoTest'); //store the parameters aParam := aParameter; aOut := aOutput; end; class function TParameterizedTestCase.Suite: TTestSuite; begin //manually create the test suite Result := TTestSuite.Create('TParameterizedTestCase'); Result.AddTest(TParameterizedTestCase.Create('', 0)); Result.AddTest(TParameterizedTestCase.Create('o', 1)); Result.AddTest(TParameterizedTestCase.Create('two', 3)); Result.AddTest(TParameterizedTestCase.Create('three', 5)); end; procedure TParameterizedTestCase.DoTest; begin //insert here the common functionality of the parameterized test //here for example we are testing the Length function: AssertEquals('test ' + aParam, aOut, length(aParam)); // notice that the 'test ' + aParam description was added to be able to //distinguish which test went wrong end; initialization //register the manually created suite GetTestRegistry.AddTest(TParameterizedTestCase.Suite); end.
_______________________________________________ fpc-pascal maillist - fpc-pascal@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-pascal