Sunday, August 31, 2008

Anonymous methods in testing / profiling - Pt. 2

In the last post, I only went into profiling. Here's a simple testing usage scenario. Suppose you want to make sure that a certain body of code throws an exception of a particular type. That means you have to wrap it up in a try / except block, and test the type of the exception. The logic to do this would be quite repetitive if it had to be expanded out long-hand. Fortunately, that's not the case:

procedure ExpectException(ExClass: ExceptClass; const Code: TProc);
var
  noThrow: Boolean;
begin
  noThrow := False;
  try
    Code;
    noThrow := True;
  except
    on e: Exception do
      if not e.ClassType.InheritsFrom(ExClass) then
        raise Exception.CreateFmt('Failed - %s expected, %s actually raised',
          [ExClass.ClassName, e.ClassName]);
  end;
  if noThrow then
    raise Exception.CreateFmt('Failed - %s not raised', 
      [ExClass.ClassName]);
end;

Here it is in use:

var
  list: TList;
begin
  list := TList.Create;
  try
    
    ExpectException(EListError, procedure
    begin
      list[42] := nil;
    end);
    
  finally
    list.Free;
  end;
end;

2 comments:

Jolyon Smith said...

Now that is quite neat. Very similar to the sort of test code I write with Smoketest:

procedure TTestCase.TestMethod;
begin
try
.. test case code
ExpectException(EAccessViolation);
except
ExpectException(EAccessViolation);
end;

The need to repeat the ExpectException() test being needed to catch both the pass (got the exception) and fail (didn't) conditions.

That repetition isn't particularly arduous but it is a little ugly.

Giel said...

This is a nice example. Thanks for the blogs, I'm almost beginning to understand these anonymous methods ;-)