Rational QualityArchitect/Java API
Version 1.0

Package com.rational.test.vp

Provides verification point services to Java test scripts.

See:
          Description

Interface Summary
DataTable A class implementing this interface encapsulates a table of string data.
VerificationPointComparator A class implementing this interface provides a method that compares two VerificationPointData objects to determine if the comparison succeeds or fails.
VerificationPointData A class implementing this interface encapsulates and serializes a single snapshot of either expected or actual data.
VerificationPointDataProvider An implementation of this class creates a Verification Point Data object based on the verification point metadata in the specialized Verification Point object.
VerificationPointDataRenderer A class implementing this interface provides the capability of displaying the data stored in the Verification Point Data class, allowing the tester to interactively accept or reject that data as the expected (baseline) data for a static verification point.
 

Class Summary
DatabaseVP This class implements a database verification point.
DatabaseVPComparator The framework calls the compare() method in this class to compare two DatabaseVPData objects.
DatabaseVPData This class encapsulates and serializes the data being verified by the database verification point.
DatabaseVPDataProvider DatabaseVPDataProvider provides the link between the DatabaseVP class and the DatabaseVPData class.
DatabaseVPDataRenderer This class implements a renderer for any class that implements the DataTable interface.
VerificationPoint This class contains the verification point's metadata -- that is, the information that determines the data to capture for this verification point.
VerificationPointProperties This class is used to store the Verification Point properties.
 

Package com.rational.test.vp Description

Provides verification point services to Java test scripts.

In functional and component testing, a verification point is a mechanism for testing, or verifying, the state of the component-under-test as a result of one or more operations. For example, in a banking application, you might want to verify that a component correctly calculates a monthly mortgage payment for a given set of inputs such as loan amount, interest rate, and life of loan.

You establish verification points in your test scripts using the classes and interfaces provided in this package. You can use the contents of this package in two ways:

Who Should Use This Package

This package and its documentation are intended for the following testing team members. Whether these different tasks are performed by the same person or different persons depends upon the requirements of your site:

Another team member referred to in this package documentation is the tester. The tester is the person who runs the test scripts that the test designer writes.

The following diagram illustrates the different roles of the test team:


Contents of Package Overview

This package overview contains the following sections:

How Data Is Verified

A verification point operates on two different types of data:

A verification point compares expected data and actual data. If the data matches (or optionally, satisfies some other condition, such as falling within an accepted range), the verification point passes. Otherwise, the verification point fails. Verification point results are automatically logged in the LogViewer.

In the following illustration, the account balance 935.49 is the expected data for a given input (an account number). In three subsequent tests, the stored expected data is compared against the actual data captured during each test. In this example, the verification point passes if the expected data matches the actual data:



Types of Verification Points

The verification point framework contained in this package provides for the three types of verification points summarized below:


Expected Data Object Actual Data Object
Static Verification Point Captured when script is first run Captured at subsequent script runs
Dynamic Verification Point Test script passes to verification point Captured at script runtime
Manual Verification Point Test script passes to verification point Test script passes to verification point


Framework for Implementing New Verification Points

The com.rational.test.vp package includes the pre-defined database verification point for verifying data in a JDBC database. This is typically the verification point you use in writing scipts for EJB testing.

If you need to use other kinds of verification points, the verification point implementer must first extend and implement the class and interfaces in the verification point framework provided in the com.rational.test.vp package.

The verification point framework contains the following class and interfaces:

Verification Point Classes

Conceptually, a verification point is made up of the following classes:

The following illustration summarizes the verification point classes:



Setting Up Verification Points in Test Scripts

This section outlines the actions that you, the test designer, need to take to set up a verification point in a test script.

Use the actions outlined below as a guideline for setting up a verification point. Other actions may be necessary to accommodate the requirements of a particular verification point implementation.

Note that much of the work that is required to perform a verification point is done for you by the verification point framework.

Setting Up a Static Verification Point

To set up a static verification point:

  1. Specify the verification point's metadata.

    The specialized VerificationPoint class encapsulates a verification point's metadata. Metadata includes the following kinds of information:

    Verification point metadata can be specified either explicitly or implicitly:

    For more information about how verification point metadata can be provided, see the VerificationPoint class.

  2. Execute the verification point.

    To execute a verification point, call the performTest() method in the specialized VerificationPoint class, as follows:

    Using the metadata in the specialized VerificationPoint class, the framework captures the actual data for the test. The framework also checks the datastore for an expected (baseline) data object to compare against the actual data:

For an example of a static verification point setup in a test script, see Example of a Static Database Verification Point.

Setting Up a Dynamic Verification Point

Setting up a dynamic verification point is similar to setting up a static verification point. However, before the test script executes the verification point, the test script must create the expected data object. The framework is responsible for capturing and building the actual data object, just as it does for a static verification point.

You create the expected data object using the appropriate implementation of the VerificationPointData interface.

Once the expected data object is created, you can pass it to the performTest() method when you execute the verification point.

For an example of a dynamic verification point setup in a test script, see Example of a Dynamic Database Verification Point.

Setting Up a Manual Verification Point

Setting up a manual verification point is similar to setting up a static verification point. However, before the test script executes the verification point, the test script must create both the expected and actual data objects.

You create the expected and actual data objects using the appropriate implementation of the VerificationPointData interface.

Once the expected and atual data objects are created, you can pass them to the performTest() method when you execute the verification point.


Implementing a New Verification Point

The verification point framework is built around an open architecture. This open architecture allows you to implement your own verification point types and execute them within the framework.

This section describes the steps necessary to implement a new verification point type. It has the following topics:

This section is intended only for implementers of new verification points. If you are a test designer who is adding existing verification point to your scripts, you can skip this section. This section assumes a sound working knowledge of Java as well as an understanding of verification points.

Note:Some of the examples in this section use the CTutil class to retrieve values from an .ini file. If you want to refer to the CTutil class code for greater understanding of the examples, you can find the code in the section CTutil Class Source Code, which is located in the Rational Test Script Services for Java online documentation.

Fundamentals for Defining a Verification Point Programmatically

To implement a new verification point, you must implement the following classes:

The following sections describe these classes.

Implementing the Verification Point Class

Your specialized Verification Point class must extend the com.rational.test.vp.VerificationPoint abstract class, as shown below, and implement all the abstract methods within it.

public class DatabaseVP extends com.rational.test.vp.VerificationPoint

Further, your Verification Point class inherits the framework’s entire behavior from this abstract base class. For details about this inherited behavior, see Integrating a Verification Point into the QualityArchitect Environment.

Your specialized Verification Point class must perform the following tasks:

  1. Define and maintain the metadata that describes the verification to be performed.

  2. Supply a UI that allows a tester to specify the metadata.

  3. Implement constructors that provide the new verification point’s name and metadata.

  4. Implement the Rational code factory methods. These framework methods automatically generate source code into a test script and are capable of creating instances of your verification point.

  5. Provide serialization services for the verification point’s metadata.

Step 1. Define and Maintain the Metadata

Your verification point must contain member variables and corresponding get/set methods for all attributes necessary to describe the verification point’s metadata.

The following example illustrates the use of get/set methods for retrieving and assigning metadata such as a JDBC user ID, password, url, and a SQL statement:

private String sSQL = ""; private String sJDBCuser = ""; private String sJDBCpassword = ""; private String sJDBCdriver = ""; private String sJDBCurl = ""; public String getSQL() { return sSQL; } public String getJDBCuser() { return sJDBCuser; } public String getJDBCpassword() { return sJDBCpassword; } public String getJDBCdriver() { return sJDBCdriver; } public String getJDBCurl() { return sJDBCurl; } public void setSQL( String sSQL ) { this.sSQL = sSQL; } public void setJDBCuser( String sJDBCuser ) { this.sJDBCuser = sJDBCuser; } public void setJDBCpassword( String sJDBCpassword ) { this.sJDBCpassword = sJDBCpassword; } public void setJDBCdriver( String sJDBCdriver ) { this.sJDBCdriver = sJDBCdriver; } public void setJDBCurl( String sJDBCurl ) { this.sJDBCurl = sJDBCurl; }

Step 2. Supply a UI to Prompt for the Metadata

If a test script executes your verification point, but the verification point’s metadata is not in the datastore, the verification point must run a UI that prompts the tester for the metadata. Specifically, you must provide the following features:

The defineVPcallback() method presents the tester with your UI that prompts for the metadata. When the metadata is retrieved, the method populates the verification point’s member variables with the metadata values — for example:

public boolean defineVPcallback()
{
	// Invoke some UI and populate the class with the VP's definition.
}

Step 3. Implement Constructors to Provide the Verification Point Name and Metadata

Implement at least two constructors that use the super keyword to call the constructor of the VerificationPoint base class, as follows:

Both constructors must pass class objects for the Verification Point Data, Verification Point Data Provider, Verification Point Data Renderer, and Verification Point Data Comparator classes you have implemented. The framework can then create instances of these classes to store, serialize, capture, display, and compare the data operated on by your verification point — for example:

public DatabaseVP( String sVPname )
	{
		super(sVPname, DatabaseVPData.class,
			DatabaseVPDataProvider.class, DatabaseVPDataRenderer.class,
			DatabaseVPComparator.class);
		setIsDefined(false);
	}

public DatabaseVP( String sVPname, String sSQL, String sJDBCuser,
		String sJDBCpassword, String sJDBCdriver, String sJDBCurl )
	{
		super(sVPname, DatabaseVPData.class,
			DatabaseVPDataProvider.class, DatabaseVPDataRenderer.class,
			DatabaseVPComparator.class);
		this.sSQL = sSQL;
		this.sJDBCuser = sJDBCuser;
		this.sJDBCpassword = sJDBCpassword;
		this.sJDBCdriver = sJDBCdriver;
		this.sJDBCurl = sJDBCurl;
		
		if ( sSQL != null && !sSQL.equals("") && sJDBCdriver != null && 
			!sJDBCdriver.equals("") && sJDBCurl != null &&
			!sJDBCurl.equals(""))
		{
			setIsDefined(true);
		}
		else
		{
			setIsDefined(false);
		}
	}

Step 4. Implement the Code Factory Methods to Automatically Generate Code

The code factory methods are similar in function to Java Beans in that both provide additional design-time behavior that is integrated with a Java development environment.

If a QualityArchitect user wants to insert your verification point into a generated test script, the QualityArchitect code generator takes the following actions:

  1. Creates an instance of the verification point (by calling the consructor that specifies just the verification point name).

  2. Calls the defineVPcallback() method for the newly created verification point object, presenting the tester with the UI you created to prompt for the verification point’s metadata.

After the tester specifies the metadata through the UI, the code generator invokes the code factory methods to produce Java source code. When inserted into the test script, this source code creates a verification point based on the metadata that the tester provided.

For information about how the code generators use the code factory methods, see Integrating a Verification Point into the QualityArchitect Environment.

To enable the code generators to insert an instance of your verification point into a test script, implement the following code factory methods:

The code generators call the codeFactory_getPrefix() and codeFactory_setPrefix() methods. You are not required to call them. However, you must call codeFactory_getPrefix() when constructing the externalized variables returned by the codeFactory_getConstructorInvocation() and codeFactory_getExternalizedInputDecl() methods.

If the code generators set a prefix, prepend the prefix to each externalized variable name used with the codeFactory_getConstructorInvocation() and codeFactory_getExternalizedInputDecl() methods. Doing so ensures that externalized variable names in different verification points within the same scope will be unique.

The following example illustrates the use of code factory methods:

public int codeFactory_getNumExternalizedInputs()
	{
		int iLines = 0;
		
		// At least 6 lines of code, 4 for JDBC connect info, 1 for VP name and 
		// 1 for SQL statement.
		iLines += 6;
		
		if ( getOptions() != 0 )
		{
			// If the user set any options, need to add another variable for that.
			iLines++; 
		}
		return iLines;
	}
	

public String codeFactory_getExternalizedInputDecl( int nInput )
	{
		String sCode = "";
		String sPrefix = this.codeFactory_getPrefix();
		
		// Out of range request gets an empty string (still valid code...)
		if ( nInput < codeFactory_getNumExternalizedInputs() )
		{
			switch ( nInput )
			{
				case 0:
					sCode = "String s" + sPrefix + "JDBCdriver = \"" + sJDBCdriver + "\";";
					break;
				case 1:
					sCode = "String s" + sPrefix + "JDBCurl = \"" + sJDBCurl + "\";";
					break;
				case 2:
					sCode = "String s" + sPrefix + "JDBCuser = \"" + sJDBCuser + "\";";
					break;
				case 3:
					sCode = "String s" + sPrefix + "JDBCpassword = \"" + sJDBCpassword + "\";";
					break;
				case 4:
					sCode = "String s" + sPrefix + "SQL = \"" + sSQL + "\";";
					break;
				case 5:
					sCode = "String s" + sPrefix + "VPname = \"" + getVPname() + "\";";
					break;
				case 6:
					sCode = "int i" + sPrefix + "Options = " + Integer.toString(getOptions()) + ";";
					break;
				default:
					sCode = "";
					break;
			}
		}
		
		return sCode;
	}
	

public String codeFactory_getConstructorInvocation()
	{
		StringBuffer sCode = new StringBuffer("");
		String sPrefix = this.codeFactory_getPrefix();
		
		sCode.append("DatabaseVP ");
		sCode.append(sPrefix);
		sCode.append(this.getVPname());
		sCode.append(" = new DatabaseVP( \"");
		sCode.append(this.getVPname());
		sCode.append("\", s");
		sCode.append(sPrefix);
		sCode.append("SQL, s");
		sCode.append(sPrefix);
		sCode.append("JDBCuser, s");
		sCode.append(sPrefix);
		sCode.append("JDBCpassword, s");
		sCode.append(sPrefix);
		sCode.append("JDBCdriver, s");
		sCode.append(sPrefix);
		sCode.append("JDBCurl");

		
		if ( this.getOptions() != 0 )
		{
			sCode.append(", i");
			sCode.append(sPrefix);
			sCode.append("Options);");
		}
		else
			sCode.append(");");
			
		return sCode.toString();
	}

Step 5. Provide Serialization Services for the Metadata

Implement readFile() and writeFile() methods to serialize verification point metadata. The metadata file is read by both the Verification Point Data Comparator class and the TestManager comparator software. Currently, the only supported metadata file format is .ini file format.

A future release of Rational QualityArchitect will support custom-built comparators in addition to the TestManager comparator. As a result, you will be able to use any metadata (and data) file format that your custom comparator supports.

When reading and writing your metadata file, store all metadata for your verification point, as well as properties for the additional [Definition] section in the .ini file, as shown in the following example:

public void writeFile(OutputStream out) throws IOException
	{
		// If there's nothing to write -- don't write anything...
		if ( sJDBCdriver == "" || sJDBCurl == "" || sSQL == "" )
			return;
		
		PrintWriter pwOut = new PrintWriter ( new BufferedWriter ( 
			new OutputStreamWriter ( out )));
			
		// Write out the [Definition] section
		pwOut.println("[Definition]");
		
		// Write the VP name
		pwOut.println("Case ID=" + this.getVPname());
		
		// Write the VP type
		pwOut.println("Type=Object Data");
		
		// Write the data test
		pwOut.println("Data Test=Contents");
		
		// Write the verification method
		if ( (getOptions() & COMPARE_CASEINSENSITIVE) != 0 )
			pwOut.println("Verification Method=CaseInsensitive");
		else
			pwOut.println("Verification Method=CaseSensitive");
		
		// Write the version string?
		
		// Write out the DatabaseVP specific section.
		pwOut.println("");
		pwOut.println("[Database VP]");
		
		// Write out the JDBC connect info
		pwOut.println("JDBCdriver=" + sJDBCdriver);
		pwOut.println("JDBCurl=" + sJDBCurl);
		pwOut.println("JDBCuser=" + sJDBCuser);
		pwOut.println("JDBCpassword=" + sJDBCpassword);
		
		// Write out the Select statement
		pwOut.println("SQL=" + sSQL);

		// Flush the output, and close the file.
		pwOut.flush();
	}

public void readFile(InputStream in) throws IOException
	{
		try
		{
			Hashtable tblINI = CTutil.mapINIfile( in );
			if ( tblINI != null )
			{
				String sDef = "Definition";
				String sDBVP = "Database VP";
				
				// Read out all the entries we care about.
				String sVerMethod = CTutil.readPrivateProfileString(tblINI, sDef,"Verification Method");
				if ( sVerMethod.equals("CaseInsensitive") )
					setOptions(getOptions()|COMPARE_CASEINSENSITIVE);
				
				sJDBCdriver = CTutil.readPrivateProfileString(tblINI, sDBVP,
																		"JDBCdriver");
				sJDBCurl = CTutil.readPrivateProfileString(tblINI, sDBVP,
																		"JDBCurl");
				sJDBCuser = CTutil.readPrivateProfileString(tblINI, sDBVP,
																		"JDBCuser");
				sJDBCpassword = CTutil.readPrivateProfileString(tblINI, sDBVP,
																		"JDBCpassword");
				sSQL = CTutil.readPrivateProfileString(tblINI, sDBVP, "SQL");
			}
		}
		catch ( IOException exc ) { }
		return;
	}

Implementing the Verification Point Data Class

Your specialized Verification Point Data class must implement the com.rational.test.vp.VerificationPointData interface and perform the following tasks:

  1. Create member variables that encapsulate the data that the verification point is comparing.

  2. Implement readFile() and writeFile() methods to perform serialization to a verification point data file.

  3. Implement the getFileExtension() method.

Step 1. Encapsulate the Data Being Compared

Create member variables that encapsulate the data that the verification point is comparing. The data encapsulated in these member variables should be exposed through public get and set methods that you implement. Doing so allows a test script to create and populate an instance of the class for use in dynamic and manual verification points.

The following example uses the public getData() and setData() methods to encapsulate the data objects being compared:

private String[] asColumns = null;
private Vector vData = null;
	
public int getNumCols()
{
	if (asColumns != null )
		return asColumns.length;
	else
		return 0;
}
	
public int getNumRows()
{
	if ( vData != null )
		return vData.size();
	else
		return 0;
}
	
public String[] getColumns()
{
	return asColumns;
}
	
public void setColumns( String[] asColumns )
{
	this.asColumns = asColumns;
}
	
public Vector getData()
{
	return vData;
}
	
public void setData( Vector vData )
{
	this.vData = vData;
}

Step 2. Serialize the Data to a Data File

Implement readFile() and writeFile() methods to serialize verification point data. The data file is read by both the Verification Point Data Comparator class and the TestManager comparator software. Currently, the only supported data file format is .csv file format.

A future release of Rational QualityArchitect will support custom-built comparators in addition to the TestManager comparator. As a result, you will be able to use any data (and metadata) file format that your custom comparator supports.

The following example illustrates reading from and writing to a .csv file:

public void writeFile(OutputStream out) throws IOException
	{
		// If there's nothing to write -- don't write anything...
		if ( asColumns == null || vData == null || asColumns.length == 0 )
			return;
		
		PrintWriter pwOut = new PrintWriter ( new BufferedWriter ( 
			new OutputStreamWriter ( out )));
		
		// First print out a line with all the column names.
		String csvColumns = "";
		int numCols = getNumCols();
		for ( int i=0; i < numCols; i++ )
		{
			if ( i > 0 )
				csvColumns = csvColumns + "," + "\"" + asColumns[i] + "\"";
			else
				csvColumns = "\"" + asColumns[i] + "\"";
		}
		pwOut.println(csvColumns);
		
		// Next print out a line for each element in our vector of data.
		int numRows = getNumRows();
		for ( int i=0; i < numRows; i++ )
		{
			Object obj = vData.elementAt(i);
			if ( obj != null )
			{
				// Verify that obj is an array of strings 
				
				String[] asData = (String[]) obj;
				if ( asData.length != numCols )
				{
					// Don't write out this row, and write an error message
					// to the log about the format of this object.
					
					// Llog warning message here.
				}
				else
				{
					String csvRow = "";
					for ( int j=0; j < numCols; j++ )
					{
						if ( j > 0 )
							csvRow = csvRow + "," + "\"" + asData[j] + "\"";
						else
							csvRow = "\"" + asData[j] + "\"";
					}
					pwOut.println(csvRow);
				}
			}
		}
		
		// Flush the output.
		pwOut.flush();
		
	}
	
	public void readFile(InputStream in) throws IOException,
								ClassNotFoundException
	{
		BufferedReader brIn = new BufferedReader ( 
			new InputStreamReader ( in ));

		// Read in the array of column names
		String sColumns = brIn.readLine();
		
		// If the file is empty, we're done.
		if ( sColumns == null || sColumns.length() == 0 )
			return;
		
		StringBuffer bufCSV = new StringBuffer(sColumns);
		StringBuffer bufElement = new StringBuffer("");
		int numCols = 0;
		boolean bMore = true;
		Vector vColumns = new Vector();
		
		while (bMore == true)
		{
			bMore = CTutil.csvGetNextElement(bufCSV, bufElement);
			String sElement = bufElement.toString();
			
			// Remove quotes around string if they are present.
			if ( sElement.startsWith("\"") && sElement.endsWith("\"") )
			{
				sElement = sElement.substring(1, sElement.length() - 1);
			}
			vColumns.addElement(sElement);
			numCols++;
		}
		
		// Turn the vector into an array of strings.
		asColumns = (String[])CTutil.toArray(vColumns, new String[1]);
		
		// Now read in all the data lines.
		String sData = "";
		Vector vRow = new Vector();
		vData = new Vector();
		
		for ( sData = brIn.readLine(); sData != null; sData = brIn.readLine() )
		{
			bufCSV = new StringBuffer(sData);
			bufElement.setLength(0);
			int numElements = 0;
			bMore = true;
			vRow.removeAllElements();

			while (bMore == true)
			{
				bMore = CTutil.csvGetNextElement(bufCSV, bufElement);
				String sElement = bufElement.toString();
				
				// Remove quotes around string if they are present.
				if ( sElement.startsWith("\"") && sElement.endsWith("\"") )
				{
					sElement = sElement.substring(1, sElement.length() - 1);
				}
				vRow.addElement(sElement);
				numElements++;
			}
			
			if ( numElements == numCols )
			{
				vData.addElement(CTutil.toArray(vRow, new String[1]));
			}
			else
			{
				// Handle the exception.
			}
		}
	}

Step 3. Provide the Extension for the Data File

Call getFileExtension() to provide the extension of the data file to the test script.

In this release of QualityArchitect, this method always returns .csv. In a future release, the method will return the file extension used by whatever data file format (for example, .csv, .dat, .xml) that you select for the data in your Verification Point Data class.

The framework creates the unique file name and data file passed to the writeFile() and readFile() methods. The getFileExtension() method tells the framework what file extension to use, as illustrated below:

public String getFileExtension()
{
	return "csv";
}

Implementing the Verification Point Data Comparator Class

Your specialized Verification Point Data Comparator class must implement the com.rational.test.vp.VerificationPointDataComparator. interface.

The only method in this interface is compare(). This method compares an expected data object with an actual data object (both of type VerificationPointData) and determines whether the test passes or fails.

The following example illustrates a comparison of two data objects:

public boolean compare( VerificationPointData vpdExpected, 
				VerificationPointData vpdActual,
				Object objOptions,
				StringBuffer sFailureDescription )
	{
		boolean bIdentical = true;
		StringBuffer bufActual = new StringBuffer();
		StringBuffer bufExpected = new StringBuffer();
		StringBuffer bufFailIndex = new StringBuffer();
		Integer iOptions;

		if ( objOptions != null )
			iOptions = (Integer) objOptions;
		else
			iOptions = new Integer(0);
			
		boolean bCaseInsensitive = (iOptions.intValue() &
				VerificationPoint.COMPARE_CASEINSENSITIVE) != 0;
		
		DatabaseVPData expected = (DatabaseVPData) vpdExpected;
		DatabaseVPData actual = (DatabaseVPData) vpdActual;
		
		if ( expected.getNumCols() != actual.getNumCols() )
		{
			String sText;
			if ( expected.getNumCols() == 0 || actual.getNumCols() == 0 )
				sText = "No column titles";
			else				
				sText = "Differing number of columns";

			sFailureDescription.insert(0, sText);
			sFailureDescription.setLength(sText.length());
			return false;
		}
		if ( expected.getNumRows() != actual.getNumRows() )
		{
			String sText = "Differing number of rows";
			sFailureDescription.insert(0, sText);
			sFailureDescription.setLength(sText.length());
			return false;
		}
		if ( compareStringArray( expected.getColumns(), actual.getColumns(),
					bCaseInsensitive, bufExpected, bufActual,
					bufFailIndex) == false )
		{
			String sText = "Column title[" + bufFailIndex.toString() + "]: expected[";
			sText += bufExpected.toString() + "], actual[" + bufActual.toString() + "].";
			sFailureDescription.insert(0, sText);
			sFailureDescription.setLength(sText.length());
			return false;
		}

		// Walk the vectors of data and compare each row.
		int numRows = expected.getNumRows();
		int numCols = expected.getNumCols();
		Vector vExpected = expected.getData();
		Vector vActual = actual.getData();
		String[] asExpected;
		String[] asActual;

		for ( int i=0; i < numRows; i++ )
		{
			Object obj = vExpected.elementAt(i);
			asExpected = (String[]) obj;

			obj = vActual.elementAt(i);
			asActual = (String[]) obj;
				
			if ( compareStringArray( asExpected, asActual, bCaseInsensitive,
					bufExpected, bufActual, bufFailIndex ) == false )
			{
				// Row + 2 -> 1 for the column titles (which show up as a row)
				// and one for 0 index into vector vs. 1 index in grid comparator.
				String sText = "Difference found in row[" + Integer.toString(i+2);
				sText += "], column[" + bufFailIndex.toString() + "].";
				sFailureDescription.insert(0, sText);
				sFailureDescription.setLength(sText.length());
				return false;
			}
		}
		
		return true;
	}
	
	private boolean compareStringArray( String[] asX, String[] asY, 
					boolean bCaseInsensitive,StringBuffer bufFailX, 
					StringBuffer bufFailY, StringBuffer bufFailIndex )
	{
		if ( asX.length != asY.length )
			return false;
			
		boolean bDifferent;
			
		for ( int i=0; i < asX.length; i++ )
		{
			if ( bCaseInsensitive )
				bDifferent = !asX[i].equalsIgnoreCase(asY[i]);
			else
				bDifferent = !asX[i].equals(asY[i]);
				
			if ( bDifferent )
			{
				bufFailIndex.insert(0, Integer.toString(i+1));
				bufFailIndex.setLength(Integer.toString(i).length());
				bufFailX.insert(0, asX[i]);
				bufFailX.setLength(asX[i].length());
				bufFailY.insert(0, asY[i]);
				bufFailY.setLength(asY[i].length());
				return false;
			}
		}
		return true;
	}

Implementing the Verification Point Data Provider Class

Your specialized Verification Point Data Provider class must implement the com.rational.test.vp.VerificationPointDataProvider interface.

The only method in this interface is captureData(). This method uses the metadata in a VerificationPoint object to construct and populate a VerificationPointData object.

The following example illustrates an implementation of the captureData() method:

public VerificationPointData captureData( java.lang.Object theObject, VerificationPoint VP )
	{
		DatabaseVP theVP = (DatabaseVP) VP;
		String sSQL = theVP.getSQL();
		String sJDBCuser = theVP.getJDBCuser();
		String sJDBCpassword = theVP.getJDBCpassword();
		String sJDBCdriver = theVP.getJDBCdriver();
		String sJDBCurl = theVP.getJDBCurl();
		int iOptions = theVP.getOptions();
	
		Connection con = theVP.getCon();
		Statement stmt = theVP.getStmt();
	
		DatabaseVPData vpsData = null;
		
		// Capture the data!
		
		if ( con == null || stmt == null )
		{
			// Create a JDBC connection and statement
			try {
				Class.forName(sJDBCdriver);

			} 
			catch(ClassNotFoundException e) {
				theVP.sFailureDescription = 
					"Database VP Error: Unable to load driver \""
					+ sJDBCdriver + "\"";
				theVP.bIsValid = false;
				return vpsData;
			}
			try {
				con = DriverManager.getConnection(sJDBCurl, sJDBCuser,sJDBCpassword);
			}
			catch(SQLException ex) {
				theVP.sFailureDescription = 
					"Database VP Error: Unable to Connect, UID = "
					+ sJDBCuser + ", PWD = " + sJDBCpassword + ", URL = " 
					+ sJDBCurl + ", Error = " + ex.getMessage();
				theVP.bIsValid = false;
				return vpsData;
			}
			try {
				stmt = con.createStatement();																
			}
			catch(SQLException ex) {
				theVP.sFailureDescription = 
						"Database VP Error: Unable to create Statement: " 
						+ ex.getMessage();
				theVP.bIsValid = false;
				return vpsData;
			}
		}
		
		// Execute the query.
		try {
			ResultSet rs = stmt.executeQuery(sSQL);
			ResultSetMetaData rsmd = rs.getMetaData();

			vpsData = new DatabaseVPData();
			int numColumns = rsmd.getColumnCount();
			String[] asColumns = new String[numColumns];
			
			// Build a String array of the Column Names
			if ( (iOptions & DatabaseVP.OPTION_TRIM) != 0 )
			{
				for (int i=0; i < numColumns; i++)
				{
					asColumns[i] = rsmd.getColumnName(i+1).trim();
				}
			}
			else
			{
				for (int i=0; i < numColumns; i++)
				{
					asColumns[i] = rsmd.getColumnName(i+1);
				}
			}
			
			// Put the column data into the VPdata object
			vpsData.setColumns(asColumns);
			
			// Build a Vector of the data elements
			Vector vData = new Vector();
			int numRows = 0;
			try {
				while( rs.next() )
				{
					String[] asData = new String[numColumns];
					if ( (iOptions & DatabaseVP.OPTION_TRIM) != 0 )
					{
						for (int j=0; j < numColumns; j++)
						{
							asData[j] = rs.getString(j+1).trim();
						}
					}
					else
					{
						for (int j=0; j < numColumns; j++)
						{
							asData[j] = rs.getString(j+1);
						}
					}
					
					// Put the array of strings into the vector at this row's index.
					vData.addElement((Object) asData);
					numRows++;
				}
			}
			catch(SQLException ex) {
				theVP.sFailureDescription = 
						"Database VP Error: Unable to walk ResultSet.  " 
						+ "Error = " + ex.getMessage();
				theVP.bIsValid = false;
				return null;
			}
			vpsData.setData(vData);
		}
		catch(SQLException ex) {
			theVP.sFailureDescription = 
						"Database VP Error: Unable to execute Query \"" 
						+ sSQL + "\", Error = " + ex.getMessage();
			theVP.bIsValid = false;
			return vpsData;
		}
		
		return vpsData;
	}

Implementing the Verification Point Data Renderer Class

Your specialized Verification Point Data Renderer class must implement the com.rational.test.vp.VerificationPointDataRenderer interface.

The only method in this interface is displayAndValidateData(). This method displays the data in a VerificationPointData object and allows the user to accept or reject that data as being correct.

The framework invokes displayAndValidateData() when both of the following conditions apply:

When both of these conditions exist, the framework captures the baseline data object and then invokes displayAndValidateData() to display the baseline data. If the tester accepts the data as being correct, the data is stored as the baseline for the static verification point. If the tester rejects the data, no baseline data is stored for the verification point, and the process is repeated the next time the verification point is executed.

In the following example, displayAndValidateData() presents the baseline dataobject vpdData to the tester for verification:

public boolean displayAndValidateData( VerificationPointData vpdData )
{
	// Pop up some UI which displays the vpdData object and prompts the
	// user to accept or reject.

	if ( bAccepted )
		return true;
	else
		return false;
}

Integrating a Verification Point into the QualityArchitect Environment

Once you’ve implemented a verification point, integrate the verification point into the QualityArchitect environment. After you do so, testers will be able to insert your verification point into a test script when they generate a test script from a Rational Rose model or when they record a test script with the Session Recorder.

To integrate your verification point with QualityArchitect, perform both of these tasks:


30-Jun-2003

Rational QualityArchitect/Java Support Home Page
Copyright (c) 2000, Rational Software Corporation