delphi form to call many functions - delphi

I created a library in delphi using Embarcadero XE2, and i want to test it's functions and procedures, but there are too many of them (more than 50). I would like to create a form to test these my library, with a list of functions on the left (with an options-like treeview), and with optional text boxes and a fireing button on the right of the form. I found a few treeview tutorial on the web, but they visualised databases with the treeview.
Treeview is not necessary, any possible enumeration of functions wolud be great.
Is there any straightforward method to test so many functions? Is there an easier way to implement this test(form)?
My library as a refractor for OpenOffice.org API, and it consists mainly of procedures, not functions, wich are hard to test with unit testing framework, and it would be great to create a GUI to test this class, because I have to check manually how they work. Is there any solution for this kind of test?

You could do this, but it's really the wrong approach. You're reinventing the wheel, and setting yourself up for missing testing things because you can't consistently and reliably repeat the tests.
Testing should be repeatable, and should always exercise as many possible testing options that can be tested each time something changes, to make sure that you don't introduce a bug (or put back in a bug you've fixed before), and to allow you to easily support new options or parameters or values.
You should use DUnit (Delphi unit testing), included in Delphi for several versions now (and available for earlier versions at SourceForge), which allows you to automate the testing process, and includes a TreeView that shows the test setups (called Suites), the individual tests that make up that suite, and the results of each test. It allows you to test things that should work and things that shouldn't (and make sure they don't), and repeat tests consistently so you don't miss anything. It also gives you information about any test that fails, along with information that helps you find (and fix) the failure condition.
In versions of Delphi that include DUnit already (which I believe is anything starting with Delphi 2007 and later), use File->New->Other->Unit Tests->Test Project to create the shell. For non class based testing, create another new unit and set up the tests manually. Here's a shell for you to begin with, with tests for two meaningless functions:
unit MyFunctionsTests;
{
Delphi DUnit Test Case Skeleton Unit
}
interface
uses
TestFramework, MyFunctions;
type
// Test methods for MyFunctions unit
// In the real world, you'd create separate test cases,
// one for tests that should pass and one for tests that
// should fail, and run them separately or in succession.
// This is just a simple shell.
TTestMyFunctions = class(TTestCase)
strict private
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestFunctionOne;
procedure TestFunctionTwo;
end;
implementation
procedure TTestMyFunctions.Setup;
begin
// Do any necessary initializations, set variables, etc.
end;
procedure TTestMyFunctions.TearDown;
begin
// Free any objects, release any memory, etc. here
end;
procedure TTestMyFunctions.TestFunctionOne;
begin
// FunctionOne takes two integers and adds them, and
// returns the sum
CheckTrue(FunctionOne(1, 1) = 2); // Test success
CheckFalse(FunctionOne(2, 2) = 5); // Test failure
end;
procedure TTestMyFunctions.TestFunctionTwo;
begin
CheckEqualsString('AB', FunctionTwo('A', 'B')); // Success
CheckFalse(FunctionTwo('B', 'A') = 'AB'); // Failure
end;
initialization
// Register any test cases with the test runner
RegisterTest(TTestMyFunctions.Suite);
end.
Use Project->Add to Project, and add your unit (MyFunctions.pas) and your new test case unit (MyFunctionTests.pas in the above shell). It should look something like this:
program MyFunctionUnitTests;
{
Delphi DUnit Test Project
-------------------------
This project contains the DUnit test framework and the GUI/Console test runners.
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
to use the console test runner. Otherwise the GUI test runner will be used by
default.
}
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
DUnitTestRunner,
MyFunctions in '..\MyFunctions.pas',
MyFunctionsTests in 'MyFunctionsTests.pas';
{$R *.RES}
begin
DUnitTestRunner.RunRegisteredTests;
end.
Now run the project, and in the window that appears click the green Play button (like the run button in the Delphi IDE) or hit F9. The treeview will show you the results (green for passed, red for failed) of the tests. If a test fails, you can view the error information for that test at the bottom of the window. (If you can't, use the View window to display the errors.)

Use Dunit instead. Really, you should. That's what it's for.
http://dunit.sourceforge.net/README.html
There is a little bit of a learning curve, but once you get it, it'll be "aahhhh!"

Related

Is it possible to use a library just for design time in delphi?

I am trying to write a component which is loading 3D objects from obj files.
I am using ToolsAPI library for GetActiveProject.FileName. I added designide.dcp to Requiers part in the bpl. I registered my object and in design when I put an instance of this object on a TViewPort3D which I put before everything is OK and I can see the object from the obj file is loaded in the scene, but when I try to compile the project I get an error that says ToolsAPI.dcu not found.
The procedure that I use for loading the obj file is (Type of Model variable is TModel3D) :
procedure TMyObject.LoadModel(fileName: string);
begin
if(csDesigning in ComponentState)then
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
This procedure is used in constructor as follow (TMyObject inherited from TDummy):
constructor TMyObject.Create(AOwner:TComponent)
begin
inherited;
Model:=TModel3D.Create(Self);
Model.Parent:=Self;
LoadModel('Object1.obj');
end;
Is there anyway to prevent using the ToolsAPI library when the host project for the component is about to compile?
I just thinking about something like directives as follow.
{$IFDEF DESIGNTIME}
uses ToolsAPI;
{$ENDIF}
But is it possible to do such a thing?
It sounds as though you are trying to compile the design time code into a run time project. Either a run time package, or an executable. That's not allowed. You simply cannot compile any of the ToolsAPI units into a project that is not a design time package.
You can certainly use conditional compilation to exclude the ToolsAPI units, but you will have to define your own conditional define. There is no built-in conditional that will serve your needs.
But using conditional compilation is probably not the best solution. Typically you would separate the code that used Tools API into distinct units, and only include those units in the design time projects.
So the code for your component would be split into, say, two units. The first unit, uMyComp.pas, say, contains the bulk of the code. This unit declares the component and provides its implementation. Nothing in uMyComp.pas makes any reference to ToolsAPI. The second unit, uMyCompReg.pas say, performs the component registration and any other tasks that require the ToolsAPI. There is a dependency between these units in that uMyCompReg.pas uses uMyComp.pas. Then your design time package will include both units, and any other projects that are not design time will include only uMyComp.pas.
You could achieve the same effect using conditionals. The design time project would define a conditional to indicate that this was design time. So the project settings might include a definition of a conditional named DESIGNTIME. Then all the code for your component would reside in a unit named uMyComp.pas, say. Any code related to design time would be conditional on DESIGNTIME. And any other projects that included uMyComp.pas would not have DESIGNTIME defined and so would omit the design time only code.
Whilst this is possible it is not, in my view, the best way to solve the problem. Indeed if you look around the wealth of open source examples of component development I'd be surprised if you found any that handled the separation of design time code from run time code using conditionals.
How would you separate the ToolsAPI code into a design time unit? Here's the problem method:
procedure TMyObject.LoadModel(fileName: string);
begin
if csDesigning in ComponentState then
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
First of all, let's look at the commonality of this code. The first think to observe is that the outsides of the call to LoadFromFile are the same. Only in the middle, the choice of directory, is there variation. So let's write it like this:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if csDesigning in ComponentState then
ModelDir := ExtractFilePath(GetActiveProject.FileName)
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
The problem for you is how to move GetActiveProject.FileName into the design time code. You need to use dependency injection (DI) to do this. Allow some other party to supply the logic. You need to make TMyObject ignorant of this particular detail. You could use a DI framework for this, but that's perhaps a little heavyweight just for this one task. So instead let's declare a class variable that holds a function pointer:
type
TMyObject = class(...)
...
public
class var GetModelDir: TFunc<string>;
end;
This function point allows other parties, external to the class, to specify how the model directory is located. Now LoadModel becomes:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if Assigned(GetModelDir) then
ModelDir := GetModelDir()
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
At this point, your code can now be used outside of a design time package. The next step is to add code to specify GetModelDir at design time. This code goes in the design time only unit that also registers the component. The obvious place for the code is in the initialization section of that unit. It looks like this:
initialization
TMyObject.GetModelDir :=
function: string
begin
Result := GetActiveProject.FileName;
end;
I've used anonymous methods here, but you could equally use method of object, or plain old functional types, depending on your Delphi version.
Yes, but preferably not with conditional defines as this would create far more complications and restrictions than it's worth.
You need to separate your code into different units according to whether it's design-time code or run-time code.
E.g. For a single component, the bulk of the (with no ToolsAPI dependency) goes into one unit.
A second unit performs component registration and perhaps provides custom design-time editors for the component.
The second unit uses the first and you have a clean separation without conditional defines.
You then create 2 separate packages: design-time and run-time.
The design time package will have a dependency on the ToolsAPI.
Make sure that none of the run-time units use any of the design-time units.
If any design-time units use run-time units (very likely) then the design-time package will require the run-time package.
With the above package structure, your application that uses your new components should only have dependencies on the run-time units.

Overriding Component Create Constructor in Separate Design-Time package

Programming Environment: Delphi 6 and upwards
I am aware of the fact that since Delphi 6, custom components must have separate design- and run-time package. All run time features of the a component must therefore be in a separate unit and packaged separately to the component's design-time package.
My problem is the following: My component has code that needs to be run both when it is created on the form at run time and, additional code that needs to be run at design-time, when the component is placed on the form. I have managed to put the run time code into the separate run time unit, package it and deploy it successfully.
However, in the separate design-time module unit, how do I reference and add the design-time code that needs to be included into the component's create constructor during design-time, when the component gets placed onto the form?
You can separate design time behaviour from run time behaviour with
if [not] (csDesigning in ComponentState) then
But if your constructor code needs the DesignIDE design time package, e.g. from the units DesignEditors, DesignIntf, etc..., then I think you are stuck. Maybe some IOTA involvement can help. But since there does not seem to exist a notifier interface for the creation of components, that would require a custom IOTAFormEditor. Not that easy, if not impossible.
Why not use callbacks?
From your designtime package initialization code do this:
unit MyDsgnUnit;
interface
//TMyHook defined in AnImplUnit
TMyDesignHandlerObject = class
procedure MyMethod(Sender:TObject;ParentForm:TObject); { must match TMyHook }
end;
implementation
uses AnImplUnit, DesignUnitNamesHere;
procedure TMyDesignHandlerObject.MyMethod(Sender:TObject);
var
newObject:TMyComponent;
begin
newObject := TMyComponent(Sender);
DoSomethingThatneedsDesigntimeStuff(newObject);
end;
finalization
ADesignHandlerObject.Free;
initialization
ADesignHandlerObject := TMyDesignHandlerObject.Create;
AnImplUnit.AfterConstructionHook := TDesignHandlerObject.MyMethod;
and in your component do something like this:
unit AnImplUnit;
interface
type
TMyHook = procedure(Sender:TObject;ParentForm:TObject) of object;
var
AfterConstructionHook:TMyHook;
implementation
...
procedure TMyComponent.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
DOMyStuff;
if Assigned(AfterConstructionHook)
AfterConstructionHook(Sender,Parent);
end;
Update Flushed out example more. There is no reason you can't add more parameters to the AfterConstructionHook, but since your reference to Sender is already of type TMyComponent, I don't see the point, when you can access everything public or protected, in TMyComponent(Sender) from within your hook function, and if you inherit locally (known as a protected-access class), you can access the protected stuff too.
Make sure your DPK defines a symbol of your choice, for example DESIGNTIME. Then you'll be able to use something like this to only include design-time units when needed:
uses Windows, Whatever, Something
{$IFDEF DESIGNTIME}
,DesignIntf
{$ENDIF}
;
Then to the same in your constructor code:
constructor TMyClass.Create(aOwner:TComponent);override;
begin
inherited;
{$IFDEF DESIGNTIME}
// I'm at design time.
{$ENDIF}
end;
When using this technique you should either use a separate DCU directory for your pacakge and your normal executable, or do a build each time you switch from the design time package to other projects. That's because Delphi will only re-build a DCU if the PAS has changed, when in this case the PAS doesn't tell the whole story, defined symbols also matter. If you have a DCU on disk that was compiled by Delphi while you were building your design time project, you might see a ToolsApi.DCU not found when trying to compile a normal project. Rebuilding re-compiles the DCU and makes the message go away. Just as well, if you re-compile (not re-build) the design time project after you built a normal project, your DCU might be stuck in it's non-DESIGNTIME state, leaving you without your special design-time behavior.
As long as your code does not require the IDE tools, like design interface and so on, then all you need to do is check the component flag and you can use it anywhere within the component, as follows...
procedure TNewEdit.Loaded;
begin
inherited;
if (csDesigning in ComponentState) then
ShowMessage('Designing')
else
ShowMessage('Running');
end;
However without really knowing what you are attempting to do, there are several doors left open... for example, if you want to change a property value at design time, and a different value at run time, then there are streaming issues you have to deal with.

DUnit tests hierarchy

Currently I am using 2-level test hierarchy in DUnit (Test Project -> Test Case -> Test method; see example below). Is it possible to introduce 3rd level or even more levels?
I build a hierarchy by putting backslashes in the `SuitePath'. For instance:
initialization
RegisterTests('Group1\Group2', [TExampleTests1.Suite,
TExampleTests2.Suite]);
RegisterTests('Group1\Group3', [TExampleTests3.Suite,
TExampleTests4.Suite]);
end.
In the end I get something like this:
A lot less mucking around than with David's way, and you can spread your group definitions across disparate units.
You can use test suites to create as many levels of nesting as you desire. The documentation offers the following example:
The TestFramework unit exposes the TTestSuite class, the class that
implements test suites, so you can create test hierarchies using more
explicit code:
The following function, UnitTests, creates a test suite and adds the
two test classes to it:
function UnitTests: ITestSuite;
var
ATestSuite: TTestSuite;
begin
ATestSuite := TTestSuite.create('Some trivial tests');
ATestSuite.addTest(TTestArithmetic.Suite);
ATestSuite.addTest(TTestStringlist.Suite);
Result := ATestSuite;
end;
Yet another way to implement the above function would be:
function UnitTests: ITestSuite;
begin
Result := TTestSuite.Create(
'Some trivial tests',
[TTestArithmetic.Suite, TTestStringlist.Suite]
);
end;
In the above example, the TTestSuite constructor adds the tests in the passed array to the
suite.
You can register a test suite created in any of the above ways by
using the same call you use to register individual test cases:
initialization
RegisterTest('Simple Test', UnitTests);
end.
When run with GUITestRunner, you will see the new hierarchy.
You can group related tests in test suites, which can be nested.
If you want to do it at run time, check out my "Open Component Test Framework (OpenCTF)" at sourceforge.

How can I test functions and procedures as they don't belong to classes in Delphi?

I have several little functions in an old Unit called Utils.pas.
Now I'd like refactoring some of them, but I think it's better to write test before. With DUnit I think it's impossible without a class.
So I'd like to know how can I test them before refactoring?
Edit:
I thought it was impossible because I was trying to add a test case in Delphi using Test Case Wizard. See the picture bellow that there aren't any classes and methods, so I'm not be able to create it.
AFAICT, DUnit does not require code under test to exist as class methods. Only the test cases themselves must be classes.
EDIT: The wizard is just a convenience. You don't have to use it.
You can't test a standalone function using the wizard but it's not a problem to test a standalone function with DUnit.
Example
//***** A Standalone function te be tested in a unit far, far away
function Add(v1, v2: Integer): Integer;
...
//***** A testclass to contain the testmethods calling our
// standalone function
TTestAdd = class(TTestcase)
published
procedure AddingComplement_ShouldEqualZero;
procedure AddingNegativeNumbers_ShouldBeLessThanZero
...
end;
implementation
procedure TTestAdd.AddingComplement_ShouldEqualZero;
begin
// Setup, Execute & Verify
CheckEquals(0, Utils.Add(-1, 1), 'Complement doesn''t add to zero');
end;
procedure TTestAdd.AddingNegativeNumbers_ShouldBeLessThanZero
begin
// Setup, Execute & Verify
CheckEquals(-3, Utils.Add(-1, -2), 'Add doesn''t add');
end;
Real code needs to be maintained. Real code has assumptions that are not well documented. Real code is changed by people who forget or never knew those assumptions. Trust the tests, dont trust the code.
Real TDD allows you to create the object and its methods before implementation. You need a clear model before you can write a test case anyway.
So generate the object(s), add the methods, parameters etc. Probably using UML2 would be best, then write the test cases for those, and then implement the objects. After that run the profiler and find out how horrible your code really is, and refactor.
As a general solution it is almost always best to write a factory object to instantiate and initialize your objects. The closer you get to core functionality the more this becomes important.
Write tests for your expected failures and exceptions. use a check to make sure.
Finally write each test and watch it fail before you write the code to make it succeed.

Is possible to include files (linking) based on a component property?

Delphi 2007/2009 odd question here:
It's possible, based on a component property defined in design-time, to include files in linking or leave them ?
Example: If I leave SomeProperty true, when compiling, the unit SomeUnit will be included into my project. Otherwise it will not be included.
My second approach to this problem is to deploy a second component, which when dropped in the form (or not) will include the unit in uses clause. But if it can be done with a property, that'll be better.
I want to avoid conditional compilation via IFDEF because that forces the component to be built every time the projects are built. Or not?
I am trying to achieve an easy way of including some units in project, and then those units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
edit: I'll stay with the component way for instance. I knew the IFDEF method and things, but that forces the component to be built everytime the projects are built. Or not?
I was trying to achieve an easy way of including some units in project, and then that units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
No.
What are you trying to solve?
You could add a postcompiling step that would optionally include some resource based on a component property - but you'd have to do some coding to implement such a feature.
You can use the {$IFDEF youridentifier} optional code {$ENDIF} method to conditionally compile data in to your application and then to enable it just go to your project options and enter youridentifier into the appropriate option field. Another method of doing this is to add the following to the top of your unit (or in an include file):
{$DEFINE youridentifier}
which will force youridentifier on. To disable, just place a period right before the $:
{.$DEFINE youridentifier}
Using these techniques its easy to conditionally bring in code or replace code with each compile.
Write an IDE add-in. Handle the "before compile" notification and check whether any forms or data modules in the project have components of the type you're interested in, and then check their properties. Based on what you find there, you can try modifying the contents of a unit to use the other unit of your choice. It certainly doesn't sound easy, but it seems possible.
Your second idea is very easy. It's exactly what the TXPManifest component does, for example. Beware that removing such a component from a form does not "unuse" the associated unit.
To conditionally add support for different databases, you might consider using run-time packages. (That's how the IDE manages to support so many different kinds of components, after all.) Put each database's custom code into a different package. Then, the databases you support are simply whichever ones have packages available at run time. No compile-time or design-time configuration required. The obstacle to this, however, is to manage which packages are available, and determine which of them are the packages that provide database support.
Your second approach will not necessarily work the way you want it to. While Delphi will helpfully add the necessary unit to your uses list when you drop the component onto the form, it will not remove the unit when you delete the component. And even if you don't use the component or any other exported entitiy from that unit, it is possible that the unit will be linked to your application anyway, when there is code in the initialization or finalization part of the unit. This is unfortunately very often the case, even when it would be possible to initialize stuff on-demand.
There is no way to do what you are asking, but something you might not be aware of is that units which are included in your uses list but never referenced will have a minimal impact on the size of your executable. The smart linker that is in Delphi does a very good job of removing code which is never used. If you are careful about your "optional unit" which is referenced by the component, and don't have any code in it which is executed globally (everything is self contained in a class or classes) then it shouldn't matter if it is in the uses clause and not used, or not in the uses clause at all.
This would easily allow you to do what I think your wanting to do, which would be to drop a component on a form which includes a unit that then can be linked to your application. Removing the component would have the effect of not linking in the unit. I believe, however, that any resources (for example forms or other items included by the $R directive) which are in the used unit would still be included in the executable.
You could use DesignIntf.RegisterSelectionEditor to register a selection editor (see comments in Delphi source code about ISelectionEditor), then use the RequiresUnits procedure to include extra units in uses clause.
TMySelectionEditor = class(TSelectionEditor)
public
procedure RequiresUnits(Proc: TGetStrProc); override;
end;
procedure Register;
implementation
procedure TMySelectionEditor.RequiresUnits(Proc: TGetStrProc);
var
comp: TMyComponent;
I: Integer;
begin
inherited RequiresUnits(Proc);
Proc('ExtraUnit');
// might be a better way of doing the code from here onwards?
if (Designer=nil)or(Designer.Root=nil) then Exit;
for I := 0 to Designer.Root.ComponentCount - 1 do
begin
if (Designer.Root.Components[i] is TMyComponent) then
begin
comp := TMyComponent(Designer.Root.Components[i]);
if comp.SampleProperty = True then
Proc('ExtraUnit2');
Proc(comp.ObjProperty.UnitName);
end;
end;
end;
procedure Register;
begin
RegisterSelectionEditor(TMyComponent, TMySelectionEditor);
end;

Resources