JaVa
   

TimedTest Example

To show the concepts of the TimedTest put into practice, we’ll create a simple JUnit test case and then decorate it with a TimedTest (see the following listing). We will step through this test in the remainder of this section.

package xptoolkit.junitperf;
import junit.framework.TestCase;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
import java.util.Random;
import java.util.Vector;
import java.util.Iterator;
import com.clarkware.junitperf.TimedTest;
public class SimpleTest extends TestCase{
 public SimpleTest(String testName){
 super(testName);
 }
 private final static int MAX_STRINGS = 55555;
 private final static String hiddenString = 'Hi Mom';
 private final static int LENGTH = hiddenString.length();
 private static String [] strings = new String[MAX_STRINGS];
 private static Vector vector = new Vector();
 private static int hiddenLocation;
 static {
 Random random = new Random();
 for (int index=0; index < MAX_STRINGS; index++){
 strings[index]=random.nextLong() + ' Not Mom';
 vector.add(strings[index]);
 }
 hiddenLocation= MAX_STRINGS-1;
 strings[hiddenLocation]=hiddenString;
 vector.setElementAt(hiddenString,hiddenLocation);
 }
 public final void testForLoop(){
 boolean found = false;
 char [] chars = null;
 int index;
 for (index=0; index < strings.length; index++){
 chars = strings[index].toCharArray();
 if (chars.length != LENGTH) return;
 if(chars[0]!='H') continue;
 if(chars[1]!='i') continue;
 if(chars[2]!=' ') continue;
 if(chars[3]!='M') continue;
 if(chars[4]!='o') continue;
 if(chars[5]!='m') continue;
 found = true;
 break;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }
 public final void testIteration(){
 boolean found=false;
 Iterator iterator = vector.iterator();
 int index=0;
 while(iterator.hasNext()){
 String string = (String) iterator.next();
 if (string.equals(hiddenString)){
 found = true;
 break;
 }
 index++;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }
 public final void testVector(){
 boolean found=false;
 Object [] items = vector.toArray();
 int index;
 String string;
 for (index=0; index < items.length; index++){
 string = (String)items[index];
 if (string.length() != LENGTH) return;
 if (string.equals(hiddenString)){
 found = true;
 break;
 }
 index++;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }
 static final int TIMES = 2000;
 public void testForLoopAlot(){
 for(int index=0; index < TIMES; index++){
 testForLoop();
 }
 }
 public void testVectorAlot(){
 for(int index=0; index < TIMES; index++){
 testVector();
 }
 }
 public void testIterationAlot(){
 for(int index=0; index < TIMES; index++){
 testIteration();
 }
 }
 public static Test suite2() {
 TestSuite suite= new TestSuite();
 suite.addTest(new SimpleTest('testIterationAlot'));
 return suite;
 }
 public static Test suite1() {
 TestSuite suite= new TestSuite();
 suite.addTest(new SimpleTest('testForLoopAlot'));
 return suite;
 }
 public static void main(String [] args){
 TestRunner.run(suite1());
 TestRunner.run(suite2()); TestRunner.run(new SimpleTest('testVectorAlot'));
 long maxElapsedTime = 5000;
 Test testCase = suite1();
 Test timedTest = new TimedTest(testCase, maxElapsedTime);
 TestRunner.run(timedTest);
 testCase = suite2();
 timedTest = new TimedTest(testCase, maxElapsedTime);
 TestRunner.run(timedTest); timedTest = new TimedTest(new SimpleTest('testVectorAlot'), maxElapsedTime);
 TestRunner.run(timedTest); }
}


The JUnit test case has two tests that iterate through a list of strings in two different ways searching for a target string. For this scenario, we decided that a vector should be passed around; after some performance analysis, we decided that the test of the code must execute in five seconds or less so that the code will not be a bottleneck in the system’s performance. The first test gets an Iterator object from a vector collection, casts each element to a string, and then checks to see if the string is equal to the hidden string as follows:

 public void testIteration(){
 boolean found=false;
 Iterator iterator = vector.iterator();
 int index=0;
 while(iterator.hasNext()){
 String string = (String) iterator.next();
 if (string.equals(hiddenString)){
 found = true;
 break;
 }
 index++;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }


Notice that the test asserts that the index is the same as the hiddenLocation, and that it asserts that the string was found. The second test has the same functionality as the first, but it tries to reduce the number of method calls, casts, and lines of executed code, as follows:

public void testForLoop(){
 boolean found = false;
 char [] chars = null;
 int index;
 for (index=0; index < strings.length; index++){
 chars = strings[index].toCharArray();
 if (chars.length != LENGTH) return;
 if(chars[0]!='H') continue;
 if(chars[1]!='i') continue;
 if(chars[2]!=' ') continue;
 if(chars[3]!='M') continue;
 if(chars[4]!='o') continue;
 if(chars[5]!='m') continue;
 found = true;
 break;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }


Because the testForLoop() method runs extremely fast, we put the tests in another loop that executes 2,000 times, as follows:

 static final int TIMES = 2000;
 public void testForLoopAlot(){
 for(int index=0; index < TIMES; index++){
 testForLoop();
 }
 }
 public void testIterationAlot(){
 for(int index=0; index < TIMES; index++){
 testIteration();
 }
 }


Now that we have created the tests, we need to give the tests enough data so that they will register a measurable time (this was not an issue when we all had 8080 PCs). The code to set up the test data is not in the setUp() method as you might expect, because code in the setUp() and tearDown() methods is counted by the TimedTest decorator class. So, when we decorate this JUnit test with a JUnitPerf TimedTest, the setup code will not be counted in the test, which makes the test of our code more accurate. The code to set up the test data is therefore all initialized in a static initializer block, as follows:

 private final static int MAX_STRINGS = 55555;
 private final static String hiddenString = 'Hi Mom';
 private final static int LENGTH = hiddenString.length();
 private static String [] strings = new String[MAX_STRINGS];
 private static Vector vector = new Vector();
 private static int hiddenLocation;
 static {
 Random random = new Random();
 for (int index=0; index < MAX_STRINGS; index++){
 strings[index]=random.nextLong() + ' Not Mom';
 vector.add(strings[index]);
 }
 hiddenLocation= MAX_STRINGS-1;
 strings[hiddenLocation]=hiddenString;
 vector.setElementAt(hiddenString,hiddenLocation);
 }


To run the previous test, we use JUnit TestRunner. We added the following code to use the TestRunner (you may recall that the TestRunner’s run() method runs a suite of tests and reports status via stdio). Notice that the main method calls junit.textui.TestRunner.run, passing it the returned value from the static suite() method:

 public static Test suite2() {
 TestSuite suite= new TestSuite();
 suite.addTest(new SimpleTest('testIterationAlot'));
 return suite;
 }
 public static Test suite1() {
 TestSuite suite= new TestSuite();
 suite.addTest(new SimpleTest('testForLoopAlot'));
 return suite;
 }
 public static void main(String [] args){
 TestRunner.run(suite1());
 TestRunner.run(suite2()); }


The default behavior of the test runner prints out the time it takes to run the test. The results from running this test are as follows:

Time: 0.01
OK (1 tests)
.
Time: 26.071
OK (1 tests)


As you can see, the testForLoop executes a lot faster than the testIteration. Now that we have two working JUnit tests, let’s decorate them with a JUnitPerf TimedTest. As you have likely realized by now, these two JUnits tests are supercilious. However, let’s pretend for the sake of learning that it is not, and that we want to get the execution time so that the test always runs in less than five seconds. To do this, we create a JUnitPerf TimedTest. First we import the TimedTest class as follows:

import com.clarkware.junitperf.TimedTest;


Then we run the forLoopTest through the TimedTest decorator, as follows:

long maxElapsedTime = 5000
Test testCase = suite1();
Test timedTest = new TimedTest(testCase, maxElapsedTime);
TestRunner.run(timedTest);


Notice that we pass the timedTest the TestSuite that has the testForLoop in it, and we tell it to fail if the operation takes longer than five seconds. Next we run the test for the testIteration:

testCase = suite2();
timedTest = new TimedTest(testCase, maxElapsedTime);
TestRunner.run(timedTest); 


Because we know that suite2(), the testForIteration test, takes about 28 seconds, we know that it will fail. Here is the output from this test:

. .
.TimedTest (WAITING): junit.framework.TestSuite@5debc3: 0 ms
. . .
.TimedTest (WAITING): junit.framework.TestSuite@218aa2: 26187 ms
1) junit.framework.TestSuite@218aa2 'Maximum elapsed time exceeded! Expected 5000ms, but was 26187ms.'
FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0


We can see that the test failed. Just for kicks, let’s try to optimize the test to pass. Suppose it would make the code much simpler if we were able to use a vector. However, instead of grabbing the iterator, we decide to see how much faster would it be if we get the array from the vector. Here is the code we add to try this new search function:

 public final void testVector(){
 boolean found=false;
 Object [] items = vector.toArray();
 int index;
 String string;
 for (index=0; index < items.length; index++){
 string = (String)items[index];
 if (string.length() != LENGTH) return;
 if (string.equals(hiddenString)){
 found = true;
 break;
 }
 index++;
 }
 assertEquals('Index', hiddenLocation, index);
 assertEquals('Found', true, found);
 }
. . .
 public void testVectorAlot(){
 for(int index=0; index < TIMES; index++){
 testVector();
 }
 }
. . .
 TestRunner.run(new SimpleTest('testVectorAlot'));
. . .
 timedTest = new TimedTest(new SimpleTest('testVectorAlot'), 
maxElapsedTime);
 TestRunner.run(timedTest); 


We set up JUnitPerf as before, but notice when we run this test it now passes, and we are still using a vector. Here is the output from this code:

.
Time: 4.617
.
TimedTest (WAITING): testVectorAlot(xptoolkit.junitperf.SimpleTest): 4606 ms Time: 4.606
OK (1 tests)


Just to cap off what we have done, we created a simple JUnit test that showed different ways to loop through a list of string searching for a string. We set up a JUnitPerf test for each test to see if each test could finish the test in under five seconds. We had two extreme version of the search. One version used a String array and a for loop, and it was really fast. The other version used the iterator utility to iterate through the strings in a vector and search for the string. In our contrived example, we decided that we had to use a vector, so we wrote another loop that met the criteria (and was a lot more readable than the really fast version), and then we used JUnitPerf TimedTest to prove that the new loop met the criteria.


JaVa
   
Comments