Jump to content
plumothy

Understanding DUnitX.Assert.WillRaise

Recommended Posts

I am new to unit testing and trying to understand how WillRaise works in DUnitX.

In the simple example below, there is only 1 function being tested:

  • it is given an integer
  • it expects the given integer to be positive so raises EMySimpleException if it is zero or less
  • otherwise, it just returns the given integer.

The first two TestCases check the return value. One of them passes and one of them fails (which is correct).

The next 2 TestCases use WillRaise to check if the correct exception is raised for a negative input.

I expect one test to pass and one to fail - but they both pass and I don't understand why.

 

I am assuming that I do not yet fully understand how to use WillRaise. Perhaps someone can point out my stupid error?

 

The code being tested:

unit MySimpleLogic;

interface

uses
    System.SysUtils;

type

  EMySimpleException = class(Exception)
  public
    constructor Create(const Msg: string); overload;
  end;

  TMySimpleObject = class
  public
    function SimpleFunctionRequiringPositiveInput(const AValue: integer): integer;
  end;

implementation

{ MySimpleObject }

function TMySimpleObject.SimpleFunctionRequiringPositiveInput(const AValue: integer): integer;
begin
  if (AValue <= 0) then
    raise EMySimpleException.Create('Negative input not allowed');
  result := Avalue;
end;

{ EMySimpleException }

constructor EMySimpleException.Create(const Msg: string);
begin
  inherited Create(Msg);
end;

end.

The unit testing code:

unit TestsForMySimpleLogic;

interface

uses
  System.SysUtils,
  DUnitX.TestFramework,
  MySimpleLogic;

type
  [TestFixture]
  TMyTestObject = class
  private
    CUT: TMySimpleObject;
  public
    [Setup]
    procedure Setup;
    [TearDown]
    procedure TearDown;

    [Test]
    [TestCase('TestPositiveInput01','1,1')]
    [TestCase('TestPositiveInput02','1,2')]
    procedure TestPositiveInput(const AValue: integer; const Expected: integer);
    [Test]
    [TestCase('NegativeValueShouldRaiseEMySimpleException', '-1,EMySimpleException')]
    [TestCase('NegativeValueShouldRaiseESomeOtherException', '-2,ESomeOtherException')]
    procedure RaiseExceptionForNegativeInput(const AValue: integer; const exceptionClass : ExceptClass);

  end;

implementation

procedure TMyTestObject.TestPositiveInput(const AValue: integer; const Expected: integer);
begin
  var Actual := CUT.SimpleFunctionRequiringPositiveInput(AValue);
    Assert.AreEqual(Expected, Actual);
end;

procedure TMyTestObject.RaiseExceptionForNegativeInput(const AValue: integer; const exceptionClass : ExceptClass);
begin
  var TestMethod: TTestLocalMethod := 
    procedure begin
    CUT.SimpleFunctionRequiringPositiveInput(AValue);
  end;
  Assert.WillRaise(TestMethod, exceptionClass, 'Incorrect Exception raised');
end;

procedure TMyTestObject.Setup;
begin
  CUT := TMySimpleObject.Create;
end;

procedure TMyTestObject.TearDown;
begin
  CUT.Free;
end;

initialization
  TDUnitX.RegisterTestFixture(TMyTestObject);

end.

 

Share this post


Link to post

Passing parameters like that does not work in DUnitX - it silently ignores the string name for the exception class and then passes nil. Calling Assert.WillRaise with nil as exceptionClass succeeds when any exception was raised.

 

@Vincent Parrett should be able to tell the best way to pass any parameters that cannot be easily converted from string.

Edited by Stefan Glienke

Share this post


Link to post

@Stefan Glienke OK, thank you, that explains my results.

I looked at the comments for WillRaise:

Quote

Checks that an exception exactly matching ExceptClass will be raised

Of course, I should have looked further than that!

Share this post


Link to post

There is no special magic that will convert a string into an exception object in Delphi, you have to do that yourself. Test cases just create an array of TValue and attempt to pass those as args, using the rtl conversion functions to convert TValuue to the argument type. This really only works for strings and ordinal types, it won't construct an object for you.

 

You could use a TTestDataProvider class, take a look at the ProviderExample unit in the examples folder. 

Edited by Vincent Parrett

Share this post


Link to post

The TTestDataProvider class looks like it will be very useful for me later. But, for now I have created a Helper for DUnitX.Assert which overrides WillRaise and takes the Exception as a string - here are the changes:

:
:
type
  TDunitXAssertHelper = class helper for DUnitX.Assert
    class procedure WillRaise(AMethod: TTestLocalMethod; const ExceptionClass: string); overload;
  end;
:
:
uses
  DUnitX.ResStrs;
:
:
procedure TMyTestObject.RaiseExceptionForNegativeInput(const AValue: integer; const ExceptionClass : string);
begin
  var TestMethod: TTestLocalMethod := 
  procedure begin
    CUT.SimpleFunctionRequiringPositiveInput(AValue);
  end;
  Assert.WillRaise(TestMethod, exceptionClass);
end;
:
:
{ TDunitXAssertHelper }

class procedure TDunitXAssertHelper.WillRaise(AMethod: TTestLocalMethod; const ExceptionClass: string);
begin
  DoAssert;
  try
    AMethod;
  except
    on E: Exception do
    begin
      Assert.AreEqual(ExceptionClass, E.ClassName );
      exit;
    end;
  end;
  Fail(SNoException, ReturnAddress);
end;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×