Our experience about software without testing is one sentence: "It does not work." But also testing is not enough. To find an bug nearby it's creation you have to run a whole set of test as often as possible and therefore you need automated support. The aim is to run the tests each day and have a guaranteed running software every evening.
The Idea is from Ronald E Jeffries, and his white paper about "Extreme-Programming" © 1997, jeffries@ic.net ic.net jeffries, cite:
>>Unit Tests
Each class must have unit tests. Every class’s unit tests must score 100%. We use Kent Beck’s public domain testing framework, augmented with a GUI that runs all the tests and shows the percent correct. At this writing, there are over 1300 unit tests, and they all run at 100 percent.
We recommend that unit tests be written before the class is written. This is a good way to focus attention on what the class is really about. In any case, a class isn’t done until its unit tests are in the test suite.
When classes are released, all unit tests must be running at 100 percent. All. That is, if the changes you make break the unit tests for some other class, the problem must be resolved before you release.
On the contrary, what if I’m not the one who broke the test, or what if someone is using my class incorrectly?
To emphasize the point: all the unit tests must run at 100 percent, all the time.<<
These test-scripts can not:
A special case of functional tests are implemented by Robert Kosara to do regression tests on dynamic web-pages. These are a separate package. Some classic test-environment for "Input-file : call : output-file" has not been implemented yet.
To run a test-script you have to run the following Steps:
The last three steps may be concated in some makefile. The generated test-script-class needs only the packages which are with the test.jar archive. For more information about the details of this code have a look at asgaard.test.Test.java and it's javadoc - documentation.
The Distribution is under GNU public license Version 2 www.gnu.org/copyleft/gpl.html, there is absolutely NO WARRANTY on this software.
A complete distribution contains on one compressed zip-archive test.zip containing the following parts:
To run this classes you need a JDK 1.1.6 or later.
Copyright: (C) 1997 THOUGHT Inc. All rights reserved. Copyright: (C) 1996 Karl Moss. All rights reserved. You may study, use, modify and distribute this example for any purpose, provided that this copyright notice appears in all copies. This example is provided WITHOUT WARRANTY either expressed or implied.
To start the TestScript-Parser type
java -cp test.jar;xml4j.jar asgaard.utils.test.TestScriptParser <xml-file>Command line parameter is only the <xml-file> which defines an test-script.
Remark: To ensure your XML-file is valid (if you don't use an validating editor) you can use a tree-viewer of your test-script which is with the xml-parser:
java -cp swing.jar;xml4j.jar;xml4jSamples.jar ui.TreeViewer <xml-file>
To test the TestScript-Parser type
java -cp test.jar;xml4j.jar asgaard.utils.test.TestScriptParser asgaard/utils/test/TestScriptParser.xmlAll instructions and information about the test are described in the head of the TestScriptParser.xml file.
TEST starting at Wed Sep 01 13:33:35 CEST 1999 JDBC-Driver SimpleText (mod.) Copyright (c) 1996 Karl Moss, 1997 THOUGHT Inc. New Logfile created: .testlog\log.sdf TEST LOG: .testlog MAILTO: klaus@ifs.tuwien.ac.at TEST#1 TST TEST:newString#1 SUCCEED [0ms] (java.lang.String.) TEST#2 TST TEST:newString#2 SUCCEED [0ms] (java.lang.String.) TEST#3 CMP TEST:compareString 1/2 SUCCEED (EQUAL==EQUAL) TEST#4 TST TEST:compStr#1 SUCCEED [0ms] (java.lang.String.compareTo) TEST#5 TST TEST:newVector#1 SUCCEED [0ms] (java.util.Vector.) TEST#6 TST TEST:addVector#1 SUCCEED [0ms] (java.util.Vector.addElement) TEST#7 TST TEST:addVector#2 SUCCEED [0ms] (java.util.Vector.addElement) TEST#8 TST TEST:getVector#1 SUCCEED [0ms] (java.util.Vector.firstElement) TEST#9 CMP TEST:compareString 1/1 SUCCEED (EQUAL==EQUAL) TEST#10 ARR TEST:getArray#1 SUCCEED TEST#11 CMP TEST:compareItem SUCCEED (B==B) TEST SUCCEED: TEST 0/11 tests failedThis is the report of the successful self-test of the Test class itself.
To write an test-script you have to use XML. You can edit an XML-file wich every ASCII-editor or you use one of the free XML-editors (have a look at www.ibm.com/developer/xml where is a large commented list of available software).
For all the details you may have a look in the commented asgaard/utils/test/UnitTest.dtd document definition file. For a quick start, here are some examples:
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <!DOCTYPE TestScript [<!ENTITY % UnitTest SYSTEM "asgaard/utils/test/UnitTest.dtd"> %UnitTest; <!ELEMENT TestScript (UnitTest)> ]>All necessary definitions are embedded into the document this way (and may be mixed with other definitions too, but this is out of our focus.
Remark: Please replace asgaard/utils/test/UnitTest.dtd by the path to the dtd you are using.
After the heading there smalles possible test-script looks like
<TestScript> <UnitTest package="some.package" name="SimpleScript"> <UnitTestScript name="SIMPLETESTSCRIPT"> <construct test="newString1" class="java.lang.String"> <param> <object class="java.lang.String">"Just some String"</object> </param> </construct> </UnitTestScript> </UnitTest> </TestScript>What happens here? A Test-Statement with the name "newString1" which carries "Just some String" as content is created using the String(java.lang.String) constructor. This test-statement is placed in a test-script named SIMPLETESTSCRIPT which is implemented in the class SimpleScript in the some.package package.
For this Example you need a simple Class "Person":
public class Person { public void Person() { super(); } public void setName(String name) { ... } public String getName() { ... } public void flush() { ... }Now we want to test these three methods:
<TestScript> <UnitTest package="some.package" name="SimpleScript2"> <UnitTestScript name="SIMPLETESTSCRIPT2"> <construct test="newPerson1" class="asgaard.lang.Person"> </construct> <method test="modPerson1" name="setName"> <instance> <reference test="newPerson1" cast="asgaard.lang.Person"/> </instance> <param> <object class="java.lang.String">"Birgit Auer"</object> </param> </method> <method test="savPerson1" name="flush"> <instance> <reference test="newPerson1" cast="asgaard.lang.Person"/> </instance> </method>First we create a Person instance with an parameter-less constructor. Then we invoke the setName method and "save" the Changes with the flush method. In those both method-calls we do not create new instances of Person but use the one we have created in the first test-statement by referencing it using the reference tag. To set the parameter of the setName method we instanciate an object as parameter-value using the object tag.
Of cause, the setName can only work if the constructor did well, otherwise we would get an NullPointerException. But to evaluate if the setName has worked well we need another mechanism:
<method test="getName1" name="getName"> <instance> <reference test="newPerson1" cast="asgaard.lang.Person"/> </instance> </method> <compare test="compareName1"> <reference test="getName1" cast="java.lang.String"/> <object class="java.lang.String">"Birgit Auer"</object> </compare> </UnitTestScript> </UnitTest> </TestScript>First we invoke the getName method and compare the result of this test-statement with the name getName1 to an String carrying the name which should be hold by the newPerson1 object.
Remark: The object tag holds native Java-code so you need to add " to Strings.
At some point you may come to the point where you get an array as result of an method-call an want to get e.g. th first item:
<construct test="newArray1" class="java.lang.Object[]"> <param> <object class="java.lang.Boolean">false</object> <object class="java.lang.Boolean">true</object> </param> </construct> <item test="firstItem1" index="0"> <reference test="newArray1" cast="java.lang.Object[]"/> </item>This dummy-example creates an array with two entries in the first test-statement. The item tag in the second statement returns the Object with the requested index, where it is accessible for former examination.
Remark: The capabilities of the constructor tag are restricted to create an Object[] array, because to invoke an Array - constructor is not straight forward coding.
To enhance the Java-code of the generated test-class use the javacode tag:
<javaCode> <main><![CDATA[ System.out.println("Hello, this is skid.Test, checking the asgaard.skid package."); System.out.println("Usage: java asgaard.skid.TestSkid <host>"); // ... // Some remote connection is established here // ... app.testIt(app); ]]></main> </javaCode>For more information about the details of this code have a look at the sources of the asgaard.test package and it's javadoc - documentation. And, of course hava a look at the UnitTest.dtd definition and the TestScript.xml example.