Hey there i have a problem with my Unit Testing in Delphi XE3 i have a project that consist of 1 MDIForm and allot of MDIChild forms then problem is that when i run test on my MDIChild forms i get this error:
TestAllDataSrouces: EInvalidOperation
at $0064346F
SetUp FAILED: Cannot create form. No MDI forms are currently active
my Setup method looks like this:
procedure TestTCustomerCard.SetUp;
begin
FCustomerCard := TCustomerCard.Create(Application);
end;
what can i do to solve this error? so far i tried:
FCustomerCard := TCustomerCard.Create(Application.MainForm);
FCustomerCard := TCustomerCard.Create(nil);
And
procedure TestTCustomerCard.SetUp;
var
a : TForm;
begin
a := TForm.Create(nil);
a.FormStyle := fsMDIForm;
FCustomerCard := TCustomerCard.Create(a);
end;
and my test is:
procedure TestTCustomerCard.TestAllDataSrouces;
var
I: Integer;
begin
for I := 0 to FCustomerCard.ComponentCount-1 do
begin
if (FCustomerCard.Components[i] is TcxLookupComboBox) then
begin
Check(TcxLookupComboBox(FCustomerCard.Components[i]).Properties.ListSource = nil,'Error no ListSource, Lookup: '+TcxLookupComboBox(FCustomerCard.Components[i]).Name+' Parent: '+TcxLookupComboBox(FCustomerCard.Components[i]).Parent.Name);
end;
if (FCustomerCard.Components[i] is TcxDBTextEdit) then
begin
Check(TcxDBTextEdit(FCustomerCard.Components[i]).DataBinding.DataSource = nil,'Error No DataSet, Text Edit: '+TcxDBTextEdit(FCustomerCard.Components[i]).Name+' Parent: '+TcxDBTextEdit(FCustomerCard.Components[i]).Parent.Name);
end;
if (FCustomerCard.Components[i] is TcxGridDBTableView) then
begin
Check(TcxGridDBTableView(FCustomerCard.Components[i]).DataController.DataSource = nil,'Error no Data Source, DB Grid View: '+TcxGridDBTableView(FCustomerCard.Components[i]).Name);
end;
end;
end;
Demo Project: Here
What you are doing is more like a functional or integration test. You are checking that your UI is correctly set up. That kind of test is different from a unit test.
Unit tests are supposed to check that if you give a module certain inputs, then they produce certain outputs. Unit tests are localized. They are meant to test the behaviour of a unit independently from other units. A UI specifically depends on other units. They take data from input devices and operate on databases and on the whole have quite complicated set of dependencies. That makes them a bad target for unit testing.
Take a look at this question - Unit tests vs Functional tests
To do the kind of testing you want, it is probably best to make your own tool that can set up the environment correctly and perform the test.
The error message pinpoints the problem. If you need an MDI child form, it must have an MDI parent form. And that parent form must be the main form of your program. Hard to achieve in a DUnit project. Your solutions appear to be:
Make the main form of your program be an MDI main form. I think that will be tricky to achieve.
Make your form under test not be an MDI child form.
Find a way to test that does not require instantiation of this form.
I have encountered the same problem, and I decided to implement advice of David Heffernan and "Make your form under test not be an MDI child form".
Here I will describe how I could reach this. I have made all changes in my testcase unit.
Make test form that inherits original MDI child form
type TTestCustomerCard = class(TCustomerCard) end;
Add this just before your test case class.
Copy dfm file or the form, say CustomerCard.dfm, to TestCustomerCard.dfm
Open TestCustomerCard.dfm in any text editor, delete line
FormStyle = fsMDIChild (because fsNormal is default value),
change first line
object CustomerCard: TCustomerCard
to
object TestCustomerCard: TTestCustomerCard
Add directive
{$R TestCustomerCard.dfm }
In your SetUp method instead of
FCustomerCard := TCustomerCard.Create(Application);
write
FCustomerCard := TTestCustomerCard.CreateNew(Application);
InitComponentRes( 'TTESTCUSTOMERCARD', FCustomerCard );
Related
The error I become is:
Object factory for class {3E9B315B-F456-4175-A864-B2573C4A2201} is missing. To register it, you can drop component [TFDGUIxWaitCursor] into your project
Well, I would like to do it if I did not have a VCL-less (not console) application ...
There is just a "script" in DPR file and that's it.
Following does not help:
wCur := TFDGUIxWaitCursor.Create(nil);
conn := TFDConnection.Create(nil);
try
.....
conn.Connected := True;
conn.ExecSQL('blah blah blah');
conn.Connected := False;
finally
conn.Free();
wCur.Free();
end;
There is no need to create TFDGUIxWaitCursor explicitly.
In your case it is enough to include FireDAC.VCLUI.Wait in uses clause of your project file. All the neccessary initializations and finalizations are performed in initialization and finalization sections of this unit.
The concept of a wait cursor is abstracted in FireDAC (with the interface IFDGUIxWaitCursor) to work within FireMonkey, VCL and console applications. Therefore there exists different implementation for each kind in different units. Depending on your app-type, you have to choose the appropriate implementation. The designer usually add the right one if you drop a FireDAC component onto a form, frame or datamodule.
Console: FireDAC.ConsoleUI.Wait
FMX: FireDAC.FMXUI.Wait
VCL: FireDAC.VCLUI.Wait
Now, I'm writing a unit test for a TFrame-derrived class...
In my Setup method, I got the following code:
procedure TestFixtureClass.Setup;
begin
FTestContainer := TContainer.Create;
FTestContainer.RegisterType<TframeClass, TframeClass>
.Implements<IFrameClass>
.AsSingleton(TRefCounting.True)
.DelegateTo(function: TframeClass
begin
// also tried: TframeClass.Create(Application);
// and: form1 := TForm1.Create(Application); TframeClass.Create(form1);
Result := TframeClass.Create(nil);
end)
.AsDefault;
FTestContainer.Build;
FSut := FTestContainer.Resolve<IFrameClass>; // Exception here
end;
TframeClass has reference counting (similar to TInterfacedObject), thats why I use TRefCounting.True in AsSingleton.
But now I got the following problem: Exception EInvalidoperation: 'Element has no parent window'.
The above TFrameClass works in the production application, but it raises the exception in the test application.
Is there a possibility to get this work, keeping the SUT (system-under-test) a TFrame-derrived class?
I've found the answer on my own...
It was no spring4d nor a real dunitx problem...
It is just, that you normally cannot create a frame in a console application (which my testing app is).
In a Console Application, Application.Handle is 0... and thats why TFrame cannot get a Handle...
So, I use a little hack (which I can accept for a non-productional testing application):
Application.Handle := GetConsoleWindow;
in the main (*.dpr) unit.
Then, it works.
I have read this article how to add a button to another application. When the Button is added to the parent application, everything seems OK, but when this Button is added to another app called Labform (TLabForm), the code after click is not executed. I created also a descendant to implement simple behavior after click, but no success:
TButton2 = class (TButton)
public
procedure Click; override;
end;
procedure TButton2.Click;
begin
inherited;
MessageBox(ParentWindow, 'Hello', 'Window', MB_OK);
end;
procedure TForm1.btn1Click(Sender: TObject);
var
Button2 : TButton2 ;
Hand: THandle;
begin
// Hand:= FindWindow('TLabForm', 'Labform'); // button added, but SHOWS NO message after click
Hand:= FindWindow('TForm1', 'Form1'); // button added, and SHOWS message after click
if Hand <> 0 then
begin
Button2 := TButton2.Create(self);
Button2.ParentWindow := hand;
Button2.BringToFront;
end
else
ShowMessage('handle not found');
end;
How to solve it?
thanx
Whilst it is technically possible to do what you want, it is excruciatingly difficult. Raymond Chen wrote about this at some length. The executive summary:
Is it technically legal to have a parent/child or owner/owned relationship between windows from different processes? Yes, it is technically legal. It is also technically legal to juggle chainsaws.
So, you are attempting something with difficulty akin to juggling chainsaws. Unless you have a deep understanding of Win32 you've got no chance of succeeding.
So, if you want to modify the GUI of an existing process, and it's not tractable to do so with code in a different process, what can you do? Well, it follows that you need to execute code inside the target process.
That's easy enough to do with DLL injection. Inject a DLL into the process and modify it's UI from that DLL. Still not trivial. You'll have the best chance of success if you subclass a window by replacing the existing window procedure with one of your own. That will allow you to run your UI modification code in the UI thread.
I'm using great TExceptionDialog from JEDI JCL package to show unhandled exceptions inside a C++ builder XE project, everything running ok so far. I've decided to enhance it a little bit by writing my own custom form to upload crash report to a server via FTP.
Problem is that I can't open my custom form from delphi PAS unit, tried to define as an external (no delphi programmer here, sorry :( ) but don't know how to properly code that. I've read lots of tutorials but couldn't find anything useful besides writing a DLL or an OLE container for my custom form, realy overkill for this project.
Question is, how can I properly execute this task? how to do ShowModal() of a form defined in a C++ unit, from a PAS delphi unit?
I've found an easy and practical way of doing it, kinda ugly but works!
Trick is to get form by iterating thru all forms with Screen.Forms object. I've set TAG property for my form to a predefined number just to get an easy id of it.
In short, inside C++ unit of my form, I'll do this:
MyForm->Tag=9999; // easy way of Iding my form
Then, inside my delphi unit of TExceptionDialog, in SEND button click method:
procedure TExceptionDialog.SendBtnClick(Sender: TObject);
var
i: integer;
form: TForm;
begin
for i := 0 to Screen.FormCount-1 do // all forms
begin
form := Screen.Forms[i]; // get a form
if(form.Tag = 9999) then // check if its my form
begin
form.ShowModal; // if its mine, call showmodal
break;
end;
end;
ModalResult := mrOk; // return to my app
end;
I'm writing a property editor for Delphi and I would like it to show up on the correct screen for multi-monitor support. In order to position it, I would like a reference to the "main" form for the Delphi IDE.
I've tried using the Application's MainForm property, and the Application object itself, but neither seems to work. I believe this is because the MainForm is actually the hidden TApplication instance referenced in this article by Nathanial Woolls (search for "application form"):
http://www.installationexcellence.com/articles/VistaWithDelphi/Original/Index.html
Does anyone know how to get a handle to the visible main form for the IDE. I'm trying to avoid something cheesy like iterating all forms and searching for "CodeGear RAD Studio" in the caption.
The IDE's main form is Application.MainForm. My quick test design package:
procedure DoStuff(Form: TCustomForm);
var
S: string;
begin
S := Form.Caption;
Form.Caption := S + ' - this one';
try
ShowMessage(Format('%s [%s] on monitor %d', [Form.Name, Form.ClassName, Form.Monitor.MonitorNum]));
finally
Form.Caption := S;
end;
end;
initialization
DoStuff(Application.MainForm);
This in my case displays "AppBuilder [TAppBuilder] on monitor 0" and I can see the " - this one" suffix in the main form's caption.
What doesn't seem to work in your case?
IIRC the main form is called TAppBuilder, so something like FindWindow('TAppBuilder',nil) might be a starting point for you.