Forum Discussion

stebi's avatar
stebi
Occasional Contributor
13 years ago

"try ... except" and Runner.CallMethod

Hi,



I have trouble with Runner.CallMethod. I'm not able to handle exceptions occuring inside the method call. See this code snippet:



procedure Foo;

begin

  raise('Foo-Exception');

end;



procedure Execute;

begin

  try

     Evaluate('Foo'); // Exception inside Foo is successfully caught

  except

     Log.Error( ExceptionMessage);

  end;



  try

     Runner.CallMethod('Foo'); 

  except

     Log.Error( ExceptionMessage); // Exception inside Foo is not caught. It turns in a "Script Runtime Error" an breaks the execution.

  end;

end;



I cannot simple use Evaluate because it's not able to call methods from units which aren't included, but Runner can. It's no option for me to include those units because it's a framework which executes my tests defined elsewhere.



Is it a bug or by intention?












  • tristaanogre's avatar
    tristaanogre
    Esteemed Contributor
    Runner.CallMethod needs to have the string passed in using the format of "UnitName.RoutineName".  Even if "Foo" is in the same unit, it needs to know the unit.  The Script Runtime Error may be due to the improper parameter you're passing in.
  • stebi's avatar
    stebi
    Occasional Contributor
    Sorry, my example was wrong. In my "real world" test I add the unit name. This should be a more correct sample to demonstrate the problem:



    [Unit1.sd]

    procedure Foo;

    begin

      raise('Foo-Exception');

    end;



    procedure Execute;

    begin

      try

         Evaluate('Foo'); // Exception inside Foo is successfully caught

      except

         Log.Error( ExceptionMessage); 

      end;



      try

         Runner.CallMethod('Unit1.Foo'); 

      except

         Log.Error( ExceptionMessage); // Exception inside Foo is not caught. It turns in a "Script Runtime Error" an breaks the execution.

      end;

    end;








  • tristaanogre's avatar
    tristaanogre
    Esteemed Contributor
    Actually, when I tried this, even the Evaluate call did not log the expected message.  I got, instead, a log message saying it was unable to evaluate Foo.  Evaluate is supposed to, as I understand it, return an object.



    I think that re-raising using Runner.CallMethod will not work because you're trying to call out to a method without going through the specific scripting engine.  Perhaps a better method would be that, in your "foo" method, instead of it being a procedure, make it a function that returns a boolean on an exception.  Then you're Runner.CallMethod can test for a true or false and, if false, log an error.
  • stebi's avatar
    stebi
    Occasional Contributor
    Note to myself: never Post a code snippet without ever executing it. 



    Ok, now I have a working code snippet. Also "Evaluate" needs the Unitname:




    procedure Foo(Message: string);

    begin

      raise('Foo-Exception ' + Message);

    end;





    procedure Execute;

    begin

      try

        Evaluate('Unit1.Foo(''From Evaluate'')'); // Exception inside Foo is successfully caught

      except

        Log.Message(ExceptionMessage); 

      end;





      try

        Runner.CallMethod('Unit1.Foo', 'From CallMethod'); 

      except

        Log.Message(ExceptionMessage); // Exception inside Foo is not caught. It turns in a "Script Runtime Error" an breaks the execution.

      end;

    end;




    The expected behavior is that "Execute" runs successfully and posts two messages to log. But the second exception stops the execution.



    Regarding the idea to avoid the exception: This is "impossible" because I architectured my tests around exceptions ;-) I write several conditions like I do in my applications unit tests: 



    CheckEquals(1, MyValue);

    CheckFileExists(...)

    Check(Condition);

    etc.



    The code should abort the current test when a condition is not met, but this should not stop other tests from starting. To respect a return value would make the tests unreadable.










  • tristaanogre's avatar
    tristaanogre
    Esteemed Contributor
    The problem with architecturing tests around exceptions is that you won't be able to easily distinguish an exception that is due to the test failure versus an exception due to a code environment problem.  I tend to use exceptions only for trapping unexpected failures (syntax problems, windows that never appear that I'm expecting, etc) while, if I'm checking something as a "works" and "doesn't work" condition, boolean returns work best.  



    Just my $0.02 really, but I think a better design that provides a tighter control is to do something like



    function Foo(TestString):boolean;

    begin

    try

    if not condition raise('yada')

    result := true;

    except

        result:=false;

    end;

    end;






    but hey, that's me.



    Unfortunately, as mentioned, because of how "Runner.CallMethod" operates, I don't think you'll be able to do exactly what you're looking for. I could be wrong, but that's been my experience.