4

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

AirWolf
  • 597
  • 7
  • 27
  • 1
    The error message explain the problem. There's no MDI parent form. – David Heffernan Mar 16 '14 at 18:01
  • is there a way of creating the form in the tests as MDIForm and not the Child? – AirWolf Mar 16 '14 at 18:06
  • I don't understand that question. – David Heffernan Mar 16 '14 at 18:08
  • is there a simple sollution to this problem? – AirWolf Mar 16 '14 at 18:13
  • Either create an MDI parent, or make the form not be an MDI child – David Heffernan Mar 16 '14 at 18:14
  • i tried that allready still doesnt work with this coed: procedure TestTCustomerCard.SetUp; var a : TForm; begin a := TForm.Create(nil); a.FormStyle := fsMDIForm; FCustomerCard := TCustomerCard.Create(a); end; – AirWolf Mar 16 '14 at 18:21
  • Off the top of my head you could change your forms from MDI forms to normal forms and the host your form on an MDI for when it's running is the normal application and create the form itself for testing. Alternatively, you might be able to put something in the FormCreate/Constructor/AfterConstruction to check if it's running in test most and change the FormStyle at that point. – Graymatter Mar 16 '14 at 18:21
  • IIRC, you can't just create the MDI form, it needs to be visible. – Graymatter Mar 16 '14 at 18:24
  • tried it still doesnt work ill make a demo project and upload it for you guys to play with it :) – AirWolf Mar 16 '14 at 18:30
  • Ok, I had a look at the source code. The MDI form needs to be your main form so you have to create it with Application.CreateForm. That means to you need to actually design a MDI form make sure your application creates it first. – Graymatter Mar 16 '14 at 18:39
  • 1
    DUnit and MDI really won't mix well. Are you testing GUI, or have you just mixed all your business logic in with your GUI? – David Heffernan Mar 16 '14 at 18:43
  • uploaded the demo project just press the File->Download – AirWolf Mar 16 '14 at 18:44
  • David Heffernan what i want is to test is all my components data-sources assigned or not because often they disappear if i close the connections form first so basically what i need is the forms components properties – AirWolf Mar 16 '14 at 18:46
  • Well there's your problem. You've mixed up business logic and GUI. Ask yourself why testing database code is related to MDI. – David Heffernan Mar 16 '14 at 20:03

3 Answers3

5

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.

Community
  • 1
  • 1
Anders E. Andersen
  • 1,635
  • 2
  • 14
  • 20
  • hmm actually makes sense :) thanks im a newbie at these tests but as i progress more in to my project i cant evade them any more because one wrong move and half of your logic is gone or in my case half of my data-sources :D – AirWolf Mar 16 '14 at 21:51
  • 1
    I don't see why a UI cannot be subjected to unit tests. Yes it has dependencies, but so does almost all code. Use mocks, for example, to deal with that. Perhaps AirWolf isn't wanting to do unit tests. But I don't agree with your seeming suggestion that UI testing is not unit testing. Read his first comment to my answer. That sounds like unit testing to me. – David Heffernan Mar 16 '14 at 22:58
  • 1
    @DavidHeffernan It is confusing to call them "unit tests" because they are very different from all common definitions of a unit test. Surely you can use a unit testing framework to do functional and integration tests, but I don't think the test themselves qualify as unit tests. – jpfollenius Mar 17 '14 at 08:00
  • @jp I'd call them unit tests if they are unit tests. Here the unit is the GUI layer. I'm not talking about integration tests. Why do you think that GUI code cannot be a unit? – David Heffernan Mar 17 '14 at 08:06
  • 1
    Well, if you **only** test the GUI you're right. But in reality, UI tests will in most cases test far more than just UI because of all the behaviour that is wired in event handlers etc. Do you really write UI tests that **only** test UI without any behaviour and just check the wiring (I am curious)? – jpfollenius Mar 17 '14 at 09:17
  • @jp I personally don't write automated unit tests for UI. We just don't have the necessary tools, or, frankly the code architecture to support such tests. The asker, as I read comments to my original answer, to be testing just the UI. And hence I changed my original answer as you can see from the edits. – David Heffernan Mar 17 '14 at 12:43
2

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.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    Yes you are right my database code is in a lower level in my program and i dont test that code what i want to test is the GUI components properties to check if the DBEdit component has a datasource assigned to it and if not then fail the test so im basically testing the components it self, if you could provide a sollution how to make my MDIChild form not be that in my DUnit project i would accept your answer. – AirWolf Mar 16 '14 at 21:05
  • Either make the main form be an MDI parent, or the child form not be an MDI child. – David Heffernan Mar 16 '14 at 21:11
  • Well it's not that simple I've been trying that whole this day with no luck if you think you can manage it download the demo project it has the same code :) – AirWolf Mar 16 '14 at 21:21
  • Why would I download code and then try to do your job? You asked a high level question. Perhaps someone else wants to write your code for you. – David Heffernan Mar 16 '14 at 21:27
  • Its not my job, the project is private and i wanted to learn Unit Testing in Delphi and i asked professionals to help me, if you don't want to help me that's fine. – AirWolf Mar 16 '14 at 21:31
1

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.

  1. Make test form that inherits original MDI child form

    type TTestCustomerCard = class(TCustomerCard) end;

    Add this just before your test case class.

  2. Copy dfm file or the form, say CustomerCard.dfm, to TestCustomerCard.dfm

  3. 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

  4. Add directive {$R TestCustomerCard.dfm }

  5. In your SetUp method instead of

    FCustomerCard := TCustomerCard.Create(Application);

    write

    FCustomerCard := TTestCustomerCard.CreateNew(Application); InitComponentRes( 'TTESTCUSTOMERCARD', FCustomerCard );

  • As I have discovered myself, this decision has one serious disadvantage, because forms OnCreate event did not got called. So i decided to make special constructor for my form `constructor TCustomerCard.CreateWithAnotherDFM; – Konstantin Popov Nov 06 '17 at 19:29
  • `constructor TCustomerCard.CreateWithAnotherDFM(AOwner: TComponent); begin CreateNew( AOwner ); InitComponentRes( 'TTESTCUSTOMERCARD', Self ); DoCreate; end;` – Konstantin Popov Nov 06 '17 at 19:38