Ervin Varga
Sourceforge user name: evarga
Thomas.P.Varghese
Sourceforge user name: thomasp20
This is a second installment from a series of articles about the JXUnit framework [1]. This article continues where the previous one [2] stopped. In order to avoid repetition, this paper will not talk about general topics pertaining to the JXUnit framework, nor about terms defined in part 1 (for example, what is a JXU command, what is a JXUC command, etc.). It is highly advisable to read the first part, if you are not comfortable with concepts used in the JXUnit framework, before continuing reading this article, though. This document describes the process of extending the framework, i.e. how to introduce new JXU and JXUC commands.
This paper assumes that 'directory' and 'folder' are synonyms, hence, they are used interchangeably in a text.
Since, we are going to tackle some more advanced techniques of the JXUnit framework's usage, knowledge of the Quick framework [3 and 4] is mandatory, and especially an acquaintance with QJML is compulsory. The other preconditions for successfully understanding the material presented here are listed in the first part titled "Overview of the JXUnit Framework". After reading this paper, you should gain enough experience to customize the JXUnit framework to better serve your particular needs. Of course, for causal usage patterns, the current set of features and commands are absolutely satisfactory.
As a good rule of thumb, before venturing into the implementation of your own extensions, first look around on the Internet, maybe there is a suitable extension already available. See a description of a generic JXU command [5], for an example how to "upgrade" the JXUnit framework to make it plausible even for functional testing.
By a new JXU command, we will mean an implementation of a JXU command not shipped with the current 3.1.3 version of the JXUnit framework. Therefore, we will treat a modified, for example, arg JXU command to be a new one in this respect. This note holds for a section named "How to Introduce a New JXUC Command", as well.
Fundamentally, the whole route of adding a new JXU command is no different from a procedure of creating a parameterized test case (a test case, which uses various data files, in our case, in XML format). As you know, in order to be able to map an arbitrary XML data construction to a particular Java object representation, and vice versa, a suitable QJML file is needed (or more precisely, a binding schema has to be available). Since, the interpretation, i.e. execution, of a 'test.jxu' file basically revolves around a data binding process, the need for an adequate QJML file is a must. Of course, at run-time a QJML file is not appropriate, thus, its compiled cousin a QIML is used. In some scenarios, where performance is crucial, a separate Java class (got by compiling a QIML file) can be utilized.
The best way to proceed further in the explanation of this topic is to unpack the 'jxunit3.jar' file. Follow the steps listed below:
Open a new command window,
Position yourself into the <JXUNIT_HOME>\JARs directory, and
Issue the following command: jar xvf jxunit3.jar (turning on a 'verbose mode' is always welcome while working with a jar tool).
The following files should be present in the 'net\sourceforge\jxunit' folder (this path is given relative to the one from which an aforementioned jar command has been summoned): 'jxu.dtd', 'jxu.qdml', 'jxu.qjml' and 'jxu.qiml'. Hopefully, no much clarification is needed what is what, and how to do conversions between different formats using Quick's tools (if you do need, please, consult references [3 and 4] before reading further; a lack of knowledge related to the Quick framework is really a major obstacle for mastering advanced techniques presented later in this paper).
TIP: It is always beneficial to extract the 'jxu.dtd' and 'jxuc.dtd' files from a 'jxunit3.jar' archive, even if you do not consider extending the JXUnit framework, and store them separately somewhere on your disk. Many good DTD aware XML editors are available (both free and commercial), and having a corresponding DTD file is handy. This same logic can be applied while working with custom XML data files, as well (there, you would produce, automatically or manually it does not matter, DTD files for diverse QJML files).
Usually, you will need to introduce changes both in 'jxu.dtd' and 'jxu.qjml' files (if you prefer working on QDML level instead of DTD one, then include the 'jxu.qdml' file into the whole story, as well). Only in rare occasions will the former remain intact; altering a behavior of an existent JXU command, without incurring syntactical changes, is an example for such a case. Of course, the strategy you choose is completely up to you. Will you make modifications only in 'jxu.qjml' file, and generate a corresponding 'jxu.dtd' file automatically, or will you manually synchronize both; that's beside the mark. Maybe a separate article, possibly titled "JXUnit Best Practices", would be a nice place for such a contemplation.
A 'jxu.qjml' file contains mappings from an XML document (such as, a 'test.jxu' file) into Java instances whose classes implement the JXTestStep interface. For an illustration, please, take a look onto the following short excerpt from a 'jxu.qjml' file:
<bean tag="jxu"> <rem>Element jxu is the root</rem> <rem>net.sourceforge.jxunit.JXDo</rem> <targetClass>net.sourceforge.jxunit.JXDo</targetClass> <elements> <item coin="testStep" optional="True" repeating="True"> <identity kind="list"/> </item> </elements> </bean> <interface label="testStep"> <targetClass>net.sourceforge.jxunit.JXTestStep</targetClass> </interface>
This is a mapping for a jxu JXU command. As you can easily spot, it describes how to create an instance of a JXDo class. The <elements> section tells, that this is a "list" like container (basically, a container realizing a List interface) whose elements are objects representing test steps of a test case in question. For each JXU command there is a similar description inside a 'jxu.qjml' file. On the other hand, a 'jxu.dtd' file contains high level depictions used by an XML parser during the XML validation process.
Working directly with a QJML file can be tedious at the beginning, in the same way as working with an XML schema definition, but after a while you can get used to it. Unfortunately, the version of Quick, shipped with a 3.1.3 version of the JXUnit framework, does not support arrays (a.k.a. <identity kind="array"/>) despite what the documentation says, so, you will need to do some extra processing to convert from a List into an adequate array. Nevertheless, a QJML binding schema is expressive enough to satisfy most of your demands. The next part will talk about implementing custom editors, in order to further boost the capabilities of the Quick framework.
All in all, after finishing the inevitable task, i.e. modifying a QJML file, you will need to generate a compiled QIML variant. If you sneak a quick look into the 'jxu.qiml' file, do not be frightened how many details are contained there. Luckily, you will never need to fiddle with this auto-generated document. Besides the 'jxu.qiml' file, the JXUnit framework contains a CreateJxu class, as well. This class has been generated from a 'jxu.qiml' file with a help of a jxu2java script (see the '<JXUNIT_HOME>\bin' directory). During execution of a test case, the engine calls (if necessary) its static method named createSchema to acquire a QDoc instance, known as schema, needed for parsing the 'test.jxu' file. Essentially, this schema can be fabricated in two ways:
by parsing the 'jxu.qiml' file, and
by invoking the static createSchema method of the CreateJxu class, hence, constructing the schema from Java (this is much faster then the previously mentioned way).
Using the CreateJxu class has one additional performance benefit over relying on a 'jxu.qiml' file, i.e. it will cache the result. In other words, schema construction will take place only on first call. Subsequent calls will just return the cached schema.
The following UML activity diagram pictures the way how a schema gets constructed and used during the execution of a 'test.jxu' file (fundamentally, it depicts the behavior of the protected runTest method of the JXTestCase class). Swimlanes show who is responsible for carrying out a particular activity.
As you can see, if a path to a 'jxu.qiml' file has been somehow given, the matching QIML file will be used (or error reported in case a path was invalid). The engine consults the value of a property named jxuSchemaName to retrieve the aforementioned path. When and where is this property initialized, at the first place? The answer to this question will lead you to the documentation for a jxuc JXUC command. The following two attributes can be specified for this JXUC command:
jxuSchemaName points to a full path where the appropriate QIML file is located; there is no default value, and
jxuSchemaClassName is a fully qualified name of the Java class responsible for schema production; the default value is (not surprisingly) net.sourceforge.jxunit.CreateJxu.
Of course, the effects of a jxuc JXUC command (besides other things, the values of above listed attributes will be put in equally named properties), can be noticed only in case, you've produced a 'test.jxuc' file in the same folder where a target 'test.jxu' file resides. As a corollary of this, you can freely designate a separate QIML file for parsing a particular 'test.jxu' file. Many different 'test.jxu' files can be present in your test suite, with diverse sets of JXU commands governed by appropriate QIML or Java class files, though. The other possible way of achieving this "mixed" situation is through the usage of a run JXU command.
Nonetheless, the tremendous flexibility envisioned in the JXUnit framework is partially ruined by an incorrect implementation. These problems are listed below:
The jxuSchemaClassName property is not exploited by the engine, and
The CreateJxu class is ordinarily not used by the engine. The only way to "force" the engine to use it, is by making a hack, i.e. giving "" as a value for a jxuSchemaName attribute (omitting a jxuSchemaName attribute in a jxuc JXUC command will produce the same outcome). Why is this happening? Simply, because the property named jxuSchemaName is automatically set to "classpath:///net/sourceforge/jxunit/jxu.qiml" (if no 'test.jxuc' file has been found for a given 'test.jxu' one) instead of leaving it undefined. This property setup occurs during the creation of a test suite.
Hopefully, these problems will be corrected in the upcoming release of the JXUnit framework. Till then be aware of these annoyances of the framework in order to save time and patience.
The following sections will try to clarify this undiluted explanation how to introduce a new JXU command in the JXUnit framework (recall, that all paths are given relative to a '<PART2_HOME>' folder, if not mentioned otherwise).
Please, unpack the following Part2SourceCode.zip archive into some folder on your hard drive. Let us call this folder as PART2_HOME. The following directory structure will be created:
<PART2_HOME>\src \tests
In the 'src' folder you will find the source code for an extension of the JXUnit framework. The extension is comprised from classes implementing new JXU & JXUC commands, and an enhanced version of the JXTestCase class. Also, it includes a code for a test step referenced from a test case for testing the arg JXU command. The 'tests' folder contains various unit tests, compliant with the JXUnit framework self-testing principle. The '<PART2_HOME>' folder holds a 'build.xml' file, as well. This is an ANT build file (something, which is missing a lot from the 3.1.3 version of the JXUnit framework). Therefore, you will need to install and configure ANT (the 'build.xml' is tested against ANT 1.4.1) on your machine (please, go to the next link in order to download a latest stable version of ANT). ANT is essentially a Java build tool; it is kind of like the make utility. You are not obligated to download the optional bundle for ANT (containing implementations for optional tasks) in order to successfully use the shipped source code of this article.
The 'src\jxunit\extensions' folder contains, besides Java sources, the following files: 'jxu.dtd', 'jxu.qjml', 'jxuc.dtd' and 'jxuc.qjml'. These files are modified versions of their original counterpart, and encompass information pertaining to the extensions developed for this article.
To see what targets are available for a build, issue the following command from a '<PART2_HOME>' folder:
ant -projecthelp
Now, here are the steps you need to perform in order to install & configure the source code (it is assumed, that you have had already configured the JXUnit framework as described in [2]):
To browse the generated documentation open the 'index.html' file located in the 'docs\api' directory.
At the end, you will have to edit the jxtest, jxu2java, and jxuc2java script files (unless you always want to rebuild the 'jxunit3.jar' file after each modification, and update the originally distributed one). The scripts have been copied into '<PART2_HOME>\bin' folder; you need to manually attach this directory to your PATH environment variable (assure that a '<PART2_HOME>\bin' folder is placed before the '<JXUNIT_HOME>\bin' one). The editing is straightforward, and equally applicable to all those scripts:
%QuickJARs%\jxunit3.jar should be changed to '<PART2_HOME>\classes';'<PART2_HOME>\JARs'
Do not try to find the LINUX versions of jxu2java and jxuc2java scripts, they are simply missing from a distribution.
Now, we are ready to proceed further with examples.
The test case for unit testing the arg JXU command can be found in the 'tests\testArg' folder. The implementation of a test step required by this test case is placed inside the 'src\jxunit\extensions\testArg' folder, and named as TestArgumentsTS. If you try to execute this test case using the original JXUnit 3.1.3 framework, you will be applauded with a NullPointerException exception. If you peek into the implementation of the arg JXU command (see the JXTestArg class), you will notice that it presumes an existence of the argList property. Apparently, this is a wrong assumption. A remedy is quite easy, and you can find it in a JXETestArg class (open the 'src\jxunit\extensions\JXETestArg.java' file).
Let us now incorporate this new JXU command into the JXUnit framework. Since, the syntax remained intact we do not need to bother with a 'jxu.dtd' file. The exact steps are given as follows (intentionally, for practicing purposes, these actions are not put inside a 'build.xml' file, i.e. no separate target exists for carrying out operations enlisted below):
Copy the 'jxu.*' files from a '<PART2_HOME>\src\jxunit\extensions' folder into '<PART2_HOME>\JARs\net\sourceforge\jxunit' one (overwrite all files),
Position yourself into the '<PART2_HOME>\JARs\net\sourceforge\jxunit',
Execute the 'cfgQjml2Qiml -in jxu.qjml -out jxu.qiml' command,
Delete the 'CreateJxu.class' file (only for safety reasons),
Copy the 'jxu2java.config' file from a '<JXUNIT_HOME>\src\net\sourceforge\jxunit' folder into the '<PART2_HOME>\JARs\net\sourceforge\jxunit' one,
Execute the jxu2java script, and
Issue the 'ant -find' command..
If you now enter the 'tests\testArg' folder, and start a jxtest script everything should be fine.
The only change done in a 'jxu.qjml' file was related to a redirection of a target class for an arg JXU command, from net.sourceforge.jxunit.JXTestArg to jxunit.extensions.JXETestArg.
The next example shows you how to implement global properties by extending the capabilities of the built-in set JXU command. The source code can be found in the 'src\jxunit\extensions\JXETestSet.java' file. The modifications inside 'jxu.dtd' and 'jxu.qjml' files can be effortlessly interrogated by searching for descriptions of the aforementioned JXU command. In order to stay compliant with the original JXUnit framework, the engine should be enhanced, as well, to be aware of the new kind of properties. Recall from the UML class diagram depicted in [2], that the engine has a responsibility to implement the storage for properties. To see what has been done in the engine, please, open the 'src\jxunit\extensions\JXETestCase.java' file with your favorite editor. Since, the source code is well documented you should not have any difficulties understanding it.
The application of the Thread-Specific Storage pattern [6] for implementing global and local properties is mandatory to ensure correctness of execution during load testing (when multiple threads are operating in parallel, see the active attribute of a directoryScan JXUC command for more information). The global properties are inheritable, in sense, that newly created threads will see all properties set by their parent, though. This does not apply in the opposite direction.
Go to the 'tests\testGlobalProperties' and execute a jxtest script from there. If you did not perform the steps enrolled in a previous section then you should do it now. As an extra step you should edit the jxtest script once more, this time changing the target class from net.sourceforge.jxunit.JXTestCase to jxunit.extensions.JXETestCase.
The unit test should finish without any problems.
Introducing a new JXUC command is basically not different than introducing a JXU one. The only difference is that you need to create an appropriate CreateJxuc class each time you make some modification to a 'jxuc.qjml' file. For further transforming a 'jxuc.qiml' file into a 'CreateJxuc.java' file there is a script called jxuc2java. Of course, a corresponding 'jxuc2java.config' file needs to be present in the working directory, and you can copy it from the '<JXUNIT_HOME>\src\net\sourceforge\jxunit' folder.
There is no way to specify different QIML files for your 'test.jxuc' ones. All 'test.jxuc' files will be processed using the schema got by calling a static createSchema method of the CreateJxuc class, though.
Adding a new JXUC command will frequently, if not always, force you to incorporate adjustments into the engine itself. As you know, a JXUC command sets up a test context, and that context is primarily helpful only to the engine (this is the case with all available JXUC commands found in the 3.1.3 version of the JXUnit framework). Unfortunately, altering the behavior of the engine is not intuitive, due to its inflexible implementation. The situation is much deteriorated with many static methods, and private member variables of the JXTestCase class. One way to overcome these obstacles is illustrated in the following example.
Nevertheless, the application of the Builder pattern [7] for the part of the engine in charge of creating a test suite, and dealing with test context information is, thus, highly advisable.
This example presents a way to implement a new subfolderTraversal JXUC command for controlling the folder tree traversal mechanism of the JXUnit framework. The source code can be found in the 'src\jxunit\extensions\JXESubfolderTraversal.java' file. The modifications inside 'jxuc.dtd' and 'jxuc.qjml' files can be effortlessly interrogated by searching for descriptions of the aforementioned JXUC command. For more detailed information regarding the usage of this JXUC command, please, consult the javadoc generated documentation.
Let us now incorporate this new JXUC command into the JXUnit framework. The exact steps are given as follows (intentionally, for practicing purposes, these actions are not put inside a 'build.xml' file, i.e. no separate target exists for carrying out operations enlisted below):
Copy the 'jxuc.*' files from a '<PART2_HOME>\src\jxunit\extensions' folder into '<PART2_HOME>\JARs\net\sourceforge\jxunit' one (overwrite all files),
Position yourself into the '<PART2_HOME>\JARs\net\sourceforge\jxunit',
Execute the 'cfgQjml2Qiml -in jxuc.qjml -out jxuc.qiml' command,
Delete the 'CreateJxuc.class' file (only for safety reasons),
Copy the 'jxuc2java.config' file from a '<JXUNIT_HOME>\src\net\sourceforge\jxunit' folder into the '<PART2_HOME>\JARs\net\sourceforge\jxunit' one,
Execute the jxuc2java script, and
Issue the 'ant -find' command..
If you enter the 'tests\testSubfolderTraversal' folder, and start a jxtest script everything should run as anticipated.
Here is a brief bulleted list of things to expect in the upcoming article:
An exact timing of the advertised article is not known, yet. Anyway, stay tuned...