In this interesting blog post on delphiXtreme I read about DUnit's built-in GUI testing capabilities (basically an alternative test case class TGUITestCase
defined in unit GUITesting
that has several utility functions for invoking actions in the GUI). I was quite happy with it until I noticed that it didn't work with modal forms. For example the following sequence won't work if the first button shows a modal configuration form:
Click ('OpenConfigButton');
Click ('OkButton');
The second Click
is only executed when the modal form is closed, which I have to do manually.
I don't know much about how modal forms work in the background but there must be some way to circumvent this behaviour. Naively, I want to somehow execute the ShowModal
"in a thread" so that the "main thread" stay responsive. Now I know that running ShowModal
in a thread will probably mess up everything. Are there any alternatives? any way to circumvent the blocking nature of a ShowModal
? Has anybody some experiences with GUI testing in Delphi?
I know about external tools (from QA or others) and we use those tools, but this question is about GUI testing within the IDE.
Thanks!
You can't test modal forms by calling
ShowModal
; because as you have quite rightly discovered, that results in your test case code 'pausing' while the modal form awaits user interaction.The reason for this is that
ShowModal
switches you into a "secondary message loop" that does not exit until the form closes.However, modal forms can still be tested.
Show
method.Show
means the form won't be closed by setting the modal result.ModalResult
is correct.WARNING
You can use this technique to test a specific modal form by explicitly showing it non-modally. However, any code under test that shows a modal form (e.g. Error Dialog) will pause your test case.
Even your sample code:
Click ('OpenConfigButton');
results in ShowModal being called, and cannot be tested in that manner.To resolve this, you need your "show commands" to be injectible into your application. If you're unfamliar with dependency injection, I recommend Misko Hevery's Clean Code Talks videos available on You Tube. Then while testing, you inject a suitable version of your "show commands" that won't show a modal form.
For example, your modal form may show an error dialog if validation fails when the Ok button is clicked.
So:
1) Define an interface (or abstract base class) to display an error messages.
2) The form you're testing can hold an injected reference to the interface (
FErrorMessage: IErrorMessage
), and use it to show an error whenever validation fails.3) The default version of IErrorMessage used / injected for production code will simply display the message as usual.
4) Test code will inject a mock version of IErrorMessage to prevent your tests from being paused.
5) Your tests can now execute cases that would ordinarily display an error message.
6) You can take the mock IErrorMessage a step further to actually verify the message text.
7) Now the earlier test becomes:
There is actually a way to test modal windows in Delphi. When a modal window is shown your application still processes windows messages so you could post a message to some helper window just before showing the modal window. Then your message would be handled from the modal loop allowing you to execute code while the modal window is still visible.
Recently I've been working on a simple library to handle this very problem. You can download the code from here: https://github.com/tomazy/DelphiUtils (see: FutureWindows.pas).
Sample usage: