| Getting started | User guide | Extending IeUnit |
With IeUnit framework a typical unit test for a web page comprises the following
steps:
For the step 1 and 6 we use methods openWindow() and closeWindow().
Also, the method seekWindow() and seekAndSetWindow()
allow a test script to attach to an existing browser window. For the step
3 to 4 we normally first find a DHTML element in the page then use DHTML APIs
to access the object. In the following sections we'll discuss these tasks in
more details.
According the DHTML model a web page is represented by a document object. The
visual elements of a web page are represented by child objects (also called
child elements) of the document object. The document object together with its
child objects forms the so called DOM tree for the web page. The most frequent
operations in an IeUnit test script are localizing objects in the DOM tree for
visual elements on a web page.
IeUnit framework makes the root document object available for test scripts
through the property this.doc within a test case that assimilated a IeUnit
object. Out-going from this root document object we can find child elements
through the DHTML APIs. There are often many different ways to find child
objects. It is however not always simple to find those elements we are
interested in.
IeUnit framework provides many methods to help find elements on web pages.
Before we get into details of different searching methods let us clarify an
important quality concept of unit test. We say that a test suite is stable if
small changes of the tested application cause only few unit tests to fail.
Higher stability means higher quality and usefulness. Nothing is more
frustrating for a software project if a large number of unit tests fail
everyday. In order to create stable test suites we should make sure that
each unit test focuses on an individual feature. There more independent the
unit tests are from each other, the more stable is the test suite.
The following are some guides to find elements with IeUnit framework:
<table>we can get the object for the td element containing text "User Name" with findByText("User"). We can get the table object with the call findParent(findByText("User"), "TABLE"). Notice that findByText() also accepts an optional index argument that can be used to specify the index of the object in case there are more than one object containing the specified text.
<tr><td>User Name</td><td><input type='text' name='username'></input><td><tr>
<tr><td>Password</td><td><input type='text' name='pwd'></input><td><tr>
</table>
If page is built with frames each frame contains a DHTML document object. Those
document objects are, more or less, independent from each other. Most APIs of
the IeDhtml class work on a single document object at a time. When the tested
page is a document with frames, the test script must call setFrame(idx_or_id)
to select one default frame (therefor the default document) before perform any
particular testing task. The selection of the default frame is persistent in
the sense that after each submission task the this.doc object will
automatically set to the document object of the selected frame by the checkSubmit()
method (more information on checkSubmit() latter).
Normally, a page has a top-level document that specifies the hierarchical
frameset+frame structure. Each frame itself refers to non-frame HTML document.
In this case we just need to call setFrame() with the index or the id
of the frame regardless of how the frame is nested in the structure. Sometimes
there are framed pages whose frame elements refers to another framed document.
In this case we can't use a single index or id to locate a frame, since the
index and id are only valid within the document containing it. We have to
use a second index or id to address the frame nested in the second document.
The syntax to use multiple indices or ids is this.setFrame("idx_or_id_1/idx_or_id_2")
where idx_or_id_1 is the index or id of the frame that contains the
second document; idx_or_id_2 is the index or id of the target frame
within the second document.
It should be pointed out that, for security reasons, Internet Explorer only
allows cross-frame scripting if the frames are from the same domain (i.e. ieunit.sourceforge.net).
This restriction leads the restriction for IeUnit that all frames of a page and
the parent window must come from one domain. Let's hope that the future
version of Internet Explorer will provide certain mechanism to allow trusted
scripts to access the document model located in foreign frames (the Internet
Explorer itself obviously can already directly access all frames).
An IeUnit test script triggers submit operations either by methods like clickLink()
and clickButton(), or directly by calling DHTML APIs like form.submit().
Since a JavaScript script runs asynchronously to the browser own process a
script normally has to wait till the response page has been received and
processed by the browser. To simplify the coding IeUnit provides the method checkSubmit()to
do all those synchronization works. Methods like clickLink() and
clickButton() implicitly call the checkSubmit() so that we
don't need call checkSubmit(). If we call DHTML APIs directly to
submit a page, e.g. call form.submit(), we need to call checkSubmit()
method afterwards.
When a script calls checkSubmit() this method basically does the following:
The short time interval for the step 1 is controlled by the IeUnit property
submitPause. The timeout value for step 2 is controlled by the
property findTimeout. The two properties submitPause and findTimeout
can be changed with the method setTime(). The wait time for
step 2 is set to 0.2*submitPause. Notice that findTimeout is
also used by other methods like waitForSuccess() and seekWindow().
In general we can roughly say that submitPause determines the minimal
pause for each submit operation whereas findTimeout determines the
maximal waiting time.
The default values for submitPause and findTimeout are 1 and
20 seconds respectively which can be changed through the configuration file Config.wjs.
The waitForSuccess() method provides a convenient way to repeatedly
try certain function till that function has succeeded (i.e. no exception gets
raised). For instance, some web page periodically refreshes its content, say
every 5 seconds. A test script can use waitForSuccess to wait till
certain content appears in the page.
The IeUnit framework also provides the sleep() method for more direct synchronization control. Scripts using a lot of sleep based synchronization are in general difficult to maintain and are instable against changes.
Inheritance has been an essential concept in most object oriented languages, and it has been proven to be a very useful concept. JavaScript does not support inheritance, but it provides some dynamic features with which we can achieve similar effect. IeUnit introduced the concept assimilation as a replacement for the inheritance concept. Both assimilation and inheritance aim to reuse code. They, however, have some subtle differences:
In IeUnit we let an object a assimilate another object b by
calling the function assimilate(a, b). All properties and methods of b
will be added to the object a. Thereafter object b should
be considered assimilated and should not be used standalone.
The function assimilate() is implemented in IeUnit.js as a top level JavaScript function. The complete interface of assimilate() is as follows:
function assimilate(dstObj, srcObj, suffix, policy)
Where dstObj is the assimilating object, srcObj is
the object to be assimilated. suffix is an optional
argument, it specifies a string suffix to be appended to member names when
adding members from srcObj to dstObj. policy
is an optional argument to resolve member name conflicts. If a member's
name of srcObj after appending possible suffix already exists
in the object dstObj we say there is name conflict. The
value of policy controls how to resolve the conflict as
follows: 0: a name conflict will cause an exception to be
thrown. 1: the conflicting member in dstObj will
silently overwritten by the conflicting member of srcObj. 2:
the conflicting member of srcObj will be silently ignored. If policy
is not specified the default value 0 will be used.
assimilate() returns the newly extended dstObj object
to the caller.
To illustrate assimilation concept let us consider the following example:
function BigCompany(companyName, headerQuarter) {
this.companyName = companyName;
this.headerQuarter = headerQuarter;
}
function SmallCompany(companyName, softwareProduct) {
this.companyName = companyName;
this.softwareProduct = softwareProduct;
}
bigCompany = new BigCompany("MegaSystems", "CityA");
smallCompany = new SmallCompany("Super QA", "Super Web Tester");
If we simply call assimilate(bigCompany, smallCompany) an exception
will be raised because of the name conflict on the member companyName.
If we call newCompany = assimilate(bigCompany, smallCompany, "Sub") the
object newCompany will have the following properties:
newCompany.companyName = "MegaSystems"
newCompany.headerQuarter = "CityA";
newCompany.companyNameSub ="Super QA";
newCompany.softwareProductSub = "Super Web Test";
If we call newCompany=assimilate(bigCompany, smallCompany, "", 1) the
object newCompany will have the following properties:
newCompany.companyName = "Super QA"If we call newCompany=assimilate(bigCompany, smallCompany, "", 2) the object newCompany will have the following properties:
newCompany.headerQuarter = "CityA";
newCompany.softwareProduct = "Super Web Test";
newCompany.companyName = "MegaSystems"
newCompany.headerQuarter = "CityA";
newCompany.softwareProduct = "Super Web Test";
It should be pointed out that assimilation is only possible in dynamic language like JavaScript that allow run-time modification of object structure. Conventional objected oriented languages like C++ or Java don't allow this, at least not in such a simple way. On the other hand, the use of this kind of dynamic features makes some work like debugging more difficult.
According to the xUnit framework a test runner is a program that helps the user
to run a set of unit tests. IeUnit provides a text based test runner IeTextRunner.wsf
that is invoked through the script engine cscrtip.exe as follows:
cscript.exe IeTextRunner.wsf <options>
The IeTextRunner.wsf program has the following options:
-help
Prints out some brief help information.
-run [<caseA>[:<test1>:<test2>:...] caseB[:<tst1>:<tst2>... ]
Runs selected test cases or tests. The argument is list of test case names, each may optionally be followed by a list of test names separated by columns. For instance, the following command executes the case AssimilationTest and the test testCheckValue of the case CaseFixtureTest:
IeTextRunner.wsf -run AssimilationTest CaseFixtureTest:testCheckValue
If no argument is provided all test cases in the current directory will be executed.
-runfiles [<a.jst> <b.jst> ...]
Runs the test cases defined in a list of .jst files. IeTextRunner will scan the .jst files and take the first function name as test case name for a .jst file. The function declaration must be a single line that starts with the keyword function.
-orgsrc <jst-file-path>
specifies the path of original source .jst file. This option is used for debugging purpose, it is only valid when a single .jst file has been specified through the -runfiles option.
-I <LibPath>
Specifies a ";" separated list of directories for the location of test scripts. The runner will load all .js scripts from the following directories: (1) The directory %IEUNIT_LIB%\lib. (2) The directories specified by the environment variable IEUNIT_LIB if it is defined. IEUNIT_LIB must point to a ";" separated list of directories. (3) The directories specified by the -I option. (4) The directory where the test case source is located. After loaded the .js files, IeTextRunner will load those .jst files from above directories which implement the test cases selected by the option -run or -runfiles. The default value of this option is %IEUNIT_HOME%/local as specified in Config.wjs file.
-l
Lists all test cases in the complete library path. See the option -I for more information about how the runner finds test cases.
-n <n>
Repeat the test <n> times.
-xml <result-xml-file-path>
This will still print out the standard test results to the console, but in addition will create an Xml file at the specified location, that conforms to the same schema used by NUnit. This enables automated testing with Nant using the Nunit2Report task
-v false
This enables running the tests without displaying the IE window. The only valid option is false. true is the default value.
For the sake of convenience IeUnit also provides some batch scripts as shortcuts
for above commands:
StartTest.bat <test-casse-name|jst-file>
Execute a test case or the test case implemented in a jst file.DebugTest.bat <jst-file|smart-bookmark-file>
Start the a test case script or smart-bookmark script under the debugger. If we need to stop at certain line in the test script we have to insert the statement "debugger" at that line.
The example LoginBookmark.sbk is located in the directory samples\LoginDemo, it reads as follows:
this.openWindow("http://ieunit.sourceforge.net/samples/LoginDemo/Login.html");
this.setField(0, "IeUnit");
this.setField(1, "DemoPwd");
this.clickButton("Login");
This script demonstrates how to automatically login into a web site that requires user name and password.
The installation procedure of IeUnit creates two Windows file extensions .jst
and .sbk. jst files will be associated with type IeUnit.JavaScriptTest.
Opening a file of this type will automatically execute the test case. sbk
files will be associated with the type IeUnit.SmartBookmark. Opening a
file of this type will automatically execute the smart bookmark script.
Windows Explorer provides a convient environment to run and test IeUnit scripts. In order to run a jst or sbk script from Windows Explorer we just need to double click the script file in Explorer.
In order to debug a IeUnit script we first right mouse click the script file to be debugged, then choose the "Debug Script" entry from the context menu.
We can also execute a set of jst files by selecting them in the Explorer window, then send them to the IeUnit target by choosing the "IeUnit" entry in the context menu.
| Getting started | User guide | Extending IeUnit |