MyGroup JUnitScript
 
   

Javascript

printer
print-friendly
PDF

Extensions Available to Javascript

args

An array of command line arguments is bound to a bean named args. If no command line arguments are used then the array will have length 0. The elements of the array are all strings.

Assertions

A command is made available for every public method of the class com.hjl.junit.Check . Thus, in a Javascript test, one may write:

assertTrue( 1 == 1 );

For a complete list of methods see the javadoc for the Check class.

assertThrows

An additional assertion is made available which is enabled by the scripting abstractions of Javascript. This assertion allows the specification that a fragment of JavaScript script (as a string) throws and exception, possibly of a set of particular types.

The syntax for this method this prototype:

void assertThrows( code, exceptionTypes, message )

The code parameter is any object that can be converted to a string. That string will be interpreted as a fragment of BeanShell script by calling eval on it. If that does not throw an exception, then the message parameter is used to initialize a junit.framework.AssertionFailedException and that is thrown to indicate failure.

If evaluating the code does throw an exception, then that exception if compared to the exceptionTypes parameter. If the exception does not match, then the message parameter is used to initialize a junit.framework.AssertionFailedException and that is thrown to indicate failure.

The matching of the exception to the exceptionTypes parameter is performed differently according to the type of the parameter.

Type of exceptionTypes Matching
null Any exception matches
Class or Javascript Constructor If the exception is an instance of that class or constructor then it matches.
Array Go through the array, for each element use the matching methodology above (for Classes and Constructors) and if the exception matches for one element of the array then it matches. Otherwise it does not match and a failure exception will be thrown.

reflector

There are some small issues that I run into when using Javascript for whitebox testing.

To solve this problem there is a class, com.hjl.junit.Reflector . This permits prototype strings to be used to identify methods, fields and constructors. A bean deriving from this class is defined under the name reflector.

Example use is:

// Get the protected getClass method of the Reflector
m = reflector.getMethod( "com.hjl.junit.Reflector.getClass( java.lang.String )" );
m.setAccessible( true );

However, it may be easier to use the Binder class below.

Binder

The reflection mechanism from the bean above can be a little clunky. Personally, I hate having to create the arrays to use reflection. A solution to this problem is presented. This involves Binder objects.

Binder objects allow you to add methods, constructors and fields to them. These additions are then callable with a syntax very similar to the normal Java syntax.

The members to be added can be specified either by passing in a java.lang.reflect.Constructor, java.lang.reflect.Field, java.lang.reflect.Method as appropriate, or by using as similar string syntax as used by the reflector bean. If a string is used, then setAccessible( true ) will be called on the member, otherwise it will not.

Binder objects also take a this object which will be used in invoking non-static members.

First we create a Binder object:

// A Binder with no 'this' object.
b1 = new Binder();
// A Binder with a 'this' object already provided.
b2 = Binder( myObject );

If we need to set or change the 'this' object associated with the binder, we can use the setThis method. Note that if only constructors or static members are to be used, then this is not neccessary.

b1.setThis( myObject );

We can add to constructors to the binder like this:

b1.addConstructor( "com.foo.MyClass()" );
// We could have passed an actual constructor instead
java.lang.reflect.Constructor constr = ...;
b1.addConstructor( constr );

This then adds a method to the binder with the following signature:

com.foo.MyClass b1.MyClass()

Thus, calling that method on b1 is equivalent to calling the constructor for MyClass. The name is inferred from the constructor. However, a binder can only have one member assigned to each name, hence each addXXX method can also take a name to assign. It is possible to set a constructor, use it, and then set another constructor to the same name.

b = new Binder();

b.addConstructor( "com.foo.MyClass()", "MyClass1" );
b.addConstructor( "com.foo.MyClass( int )", "MyClass2" );
b.addConstructor( "com.foo.MyClass( String )", "MyClass3" );
b.addConstructor( "com.foo.MyClass( MyClass )", "MyClass4" );

mc1 = b.MyClass1();
mc2 = b.MyClass2( 10 );
mc3 = b.MyClass3( "hello" );
mc3 = b.MyClass4( mc1 );

Methods are handled in a similar way, but one must remember to set the 'this' object where appropriate.

b = new Binder();

b.addMethod( "com.foo.MyClass.aStaticMethod( int, java.lang.String )" );
b.aStaticMethod( 10, "hello" );

b.addMethod( "java.lang.MyClass.aMethod( int, java.lang.String )" );

b.setThis( new Packages.com.foo.MyClass( ));
b.aMethod( 10, "hello" );

Fields however have a slightly different syntax. Fields are represented by a pair of methods, not dissimilar to normal getters and setters. However, the names do not begin with get or set. So, calling:

b = new Binder();

b.addField( "com.foo.MyClass.aField" );

Will add two methods to b (assuming the field has type T):

T b.aField(); // getter
void b.aField( T t ); // setter

Giving usage examples like this:

b = new Binder();

b.addField( "com.foo.MyClass.aStaticField" ); // Assume int for the example
b.addField( "com.foo.MyClass.aField" ); // Assume int for the example

b.aStaticField( 10 );
assertEquals( b.aStaticField(), 10 );

b.setThis( new Packages.com.foo.MyClass( ));
b.aField( 10 );
assertEquals( b.aField(), 10 );
Warning
Name mangling is used to store book keeping information in the binder object. The names of these data all begin with _$_$_$. So, if you intend to use such names, you might have to use conventional reflection.

One final problem that can be raised is when the names we wish to add to the binder conflict with the initial methods, setThis, addConstructor, addField or addMethod. This problem may be circumvented most simply by using the additional nameargument of the binder object. However another solution is to use the predefined Binder static methods.

The methods on the constructor are the same as those on an instance, except that they take a binder object as the first parameter.