Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I write 'parameterized' tests in DUnit

Tags:

I am using DUnit to test a Delphi library. I sometimes run into cases, where i write several very similar tests to check multiple inputs to a function.

Is there a way to write (something resembling) a parameterized test in DUnit? For instance specifying an input and expected output to a suitable test procedure, then running the test suite and getting feedback on which of the multiple runs of the test failed?

(Edit: an example)

For example, suppose I had two tests like this:

procedure TestMyCode_WithInput2_Returns4(); var   Sut: TMyClass;   Result: Integer; begin   // Arrange:   Sut := TMyClass.Create;    // Act:   Result := sut.DoStuff(2);    // Assert   CheckEquals(4, Result); end;  procedure TestMyCode_WithInput3_Returns9(); var   Sut: TMyClass;   Result: Integer; begin   // Arrange:   Sut := TMyClass.Create;    // Act:   Result := sut.DoStuff(3);    // Assert   CheckEquals(9, Result); end; 

I might have even more of these tests that do exactly the same thing but with different inputs and expectations. I don't want to merge them into one test, because I would like them to be able to pass or fail independently.

like image 634
Mathias Falkenberg Avatar asked Jan 25 '12 08:01

Mathias Falkenberg


People also ask

How do you write parameterized test cases in junit5?

Writing Our First Parameterized TestsAdd a new test method to our test class and ensure that this method takes a String object as a method parameter. Configure the display name of the test method. Annotate the test method with the @ParameterizedTest annotation. This annotation identifies parameterized test methods.

Does jest support parameterized tests?

Jest has a built-in support for tests parameterized with data table that can be provided either by an array of arrays or as tagged template literal.

How is a parameterized test annotation in JUnit?

JUnit 4 has introduced a new feature called parameterized tests. Parameterized tests allow a developer to run the same test over and over again using different values. There are five steps that you need to follow to create a parameterized test. Annotate test class with @RunWith(Parameterized.


2 Answers

You can use DSharp to improve your DUnit tests. Especially the new unit DSharp.Testing.DUnit.pas (in Delphi 2010 and higher).

Just add it to your uses after TestFramework and you can add attributes to your test case. Then it could look like this:

unit MyClassTests;  interface  uses   MyClass,   TestFramework,   DSharp.Testing.DUnit;  type   TMyClassTest = class(TTestCase)   private     FSut: TMyClass;   protected     procedure SetUp; override;     procedure TearDown; override;   published     [TestCase('2;4')]     [TestCase('3;9')]     procedure TestDoStuff(Input, Output: Integer);   end;  implementation  procedure TMyClassTest.SetUp; begin   inherited;   FSut := TMyClass.Create; end;  procedure TMyClassTest.TearDown; begin   inherited;   FSut.Free; end;  procedure TMyClassTest.TestDoStuff(Input, Output: Integer); begin   CheckEquals(Output, FSut.DoStuff(Input)); end;  initialization   RegisterTest(TMyClassTest.Suite);  end. 

When you run it your test looks like this:

enter image description here

Since attributes in Delphi just accept constants the attributes just take the arguments as a string where the values are separated by a semicolon. But nothing prevents you from creating your own attribute classes that take multiple arguments of the correct type to prevent "magic" strings. Anyway you are limited to types that can be const.

You can also specify the Values attribute on each argument of the method and it gets called with any possible combination (as in NUnit).

Referring to the other answers personally I want to write as little code as possible when writing unit tests. Also I want to see what the tests do when I look at the interface part without digging through the implementation part (I am not going to say: "let's do BDD"). That is why I prefer the declarative way.

like image 111
Stefan Glienke Avatar answered Oct 25 '22 08:10

Stefan Glienke


I think you are looking for something like this:

unit TestCases;  interface  uses   SysUtils, TestFramework, TestExtensions;  implementation  type   TArithmeticTest = class(TTestCase)   private     FOp1, FOp2, FSum: Integer;     constructor Create(const MethodName: string; Op1, Op2, Sum: Integer);   public     class function CreateTest(Op1, Op2, Sum: Integer): ITestSuite;   published     procedure TestAddition;     procedure TestSubtraction;   end;  { TArithmeticTest }  class function TArithmeticTest.CreateTest(Op1, Op2, Sum: Integer): ITestSuite; var   i: Integer;   Test: TArithmeticTest;   MethodEnumerator: TMethodEnumerator;   MethodName: string; begin   Result := TTestSuite.Create(Format('%d + %d = %d', [Op1, Op2, Sum]));   MethodEnumerator := TMethodEnumerator.Create(Self);   Try     for i := 0 to MethodEnumerator.MethodCount-1 do begin       MethodName := MethodEnumerator.NameOfMethod[i];       Test := TArithmeticTest.Create(MethodName, Op1, Op2, Sum);       Result.addTest(Test as ITest);     end;   Finally     MethodEnumerator.Free;   End; end;  constructor TArithmeticTest.Create(const MethodName: string; Op1, Op2, Sum: Integer); begin   inherited Create(MethodName);   FOp1 := Op1;   FOp2 := Op2;   FSum := Sum; end;  procedure TArithmeticTest.TestAddition; begin   CheckEquals(FOp1+FOp2, FSum);   CheckEquals(FOp2+FOp1, FSum); end;  procedure TArithmeticTest.TestSubtraction; begin   CheckEquals(FSum-FOp1, FOp2);   CheckEquals(FSum-FOp2, FOp1); end;  function UnitTests: ITestSuite; begin   Result := TTestSuite.Create('Addition/subtraction tests');   Result.AddTest(TArithmeticTest.CreateTest(1, 2, 3));   Result.AddTest(TArithmeticTest.CreateTest(6, 9, 15));   Result.AddTest(TArithmeticTest.CreateTest(-3, 12, 9));   Result.AddTest(TArithmeticTest.CreateTest(4, -9, -5)); end;  initialization   RegisterTest('My Test cases', UnitTests);  end. 

which looks like this in the GUI test runner:

enter image description here

I'd be very interested to know if I have gone about this in a sub-optimal way. DUnit is so incredibly general and flexible that whenever I use it I always end up feeling that I've missed a better, simpler way to solve the problem.

like image 22
David Heffernan Avatar answered Oct 25 '22 10:10

David Heffernan