Test harness

Created 24th June, 2006 08:37 (UTC), last edited 28th June, 2006 04:11 (UTC)

To start I've created a new blank solution and then added a new project to that. I've chosen ATL project and left the application settings at their default values. This has created two projects—VectorBowling which I will use and VectorBowlingPS (merges proxy and stub code) which I will ignore.

The first step of building the project works fine. So far so good.

The COM class we are using to drive the tests

To this I need to add an ATL Simple Object which will be the interface to the core implementation. I just give it the name Game as outlined in the UML and this compiles without problem too.

Next I want to build a Test.wsf (a script file) that will use the COM instances—this is going to be the first code that I actually have to write.

<?xml version="1.0" encoding="utf-8"?>
<package><job>
	<runtime>
		<description>Test the vector bowling code.</description>
	</runtime>

<script language="jscript"><![CDATA[

var game = WScript.CreateObject( "VectorBowling.Game" );

]]></script>
</job></package>

Before I get this into a better test harness I'm going to add the two public members to the Game using the wizards.

For Game.score I'm going to start with just returning zero:

STDMETHODIMP CGame::get_score(LONG* pVal)
{
	*pVal = 0;

	return S_OK;
}

And for Game.addBall() I will leave the wizard generated code as is. This will give me enough to write a proper test function in Test.wsf.

The test function I ended up with is:

function Test( score, balls ) {
	var game = WScript.CreateObject( "VectorBowling.Game" );
	for ( var ball in balls )
		game.addBall( balls[ ball ] );
	var calculated1 = game.score, calculated2 = game.score;
	if ( calculated1 == score && calculated2 == score )
		WScript.Echo( "Pass" );
	else
		WScript.Echo( "Failed: " + calculated1 + "/" + calculated2 + " not " + score + ": " + balls.toString() );
}

Note that I grab the calculation twice and make sure they are both correct. The COM objects I'm using as the test interface have internal state and therefore incorrect handling of that state could lead to it becoming corrupt. Equally some future refactoring may try to do some clever caching and if this goes wrong it is possible to conceive that calculated1 and calculated2 might be different.

The tests can now be expressed like the following:

Test( 0, [] );
Test( 0, [ 0, 0, 0 ] );
Test( 10, [ 10 ] );
Test( 10, [ 10, 0 ] );

As expected our null implementation works on the first two and fails on the last two:

Pass
Pass
Failed: 0/0 not 10: 10
Failed: 0/0 not 10: 10,0

The final part of test harness is now to build up the vector array as Game.addBall() is called. The scores are easy to store within the COM instance:

std::vector< int > balls;

And the scores are easy to append to the vector:

STDMETHODIMP CGame::addBall(LONG pins)
{
	balls.push_back( pins );

	return S_OK;
}

I can now add a prototype and a null implementation for my scoring function to the project.

First thing is to update the Game.score function to use the function we will be writing:

STDMETHODIMP CGame::get_score(LONG* pVal)
{
	*pVal = calculate_score( balls );

	return S_OK;
}

And a simple null implementation of the calculation is a good starting point:

int calculate_score( const std::vector< int > &balls ) {
	return 0;
}

And again, the first two tests pass and the second two fail. Great. Now is the time that I need to start thinking properly.

The complete test file (with some extra tests in it) is:

<?xml version="1.0" encoding="utf-8"?>
<package><job>
	<runtime>
		<description>Test the vector bowling code.</description>
	</runtime>

<script language="javascript"><![CDATA[


Test( 0, [] );
Test( 0, [ 0, 0, 0 ] );
Test( 10, [ 10 ] );
Test( 10, [ 10, 0 ] );
Test( 30, [ 10, 10 ] );
Test( 90, [ 8,1, 7,2, 6,3, 5,4, 4,5, 3,6, 2,7, 1,8, 2,7, 3,6 ] );
Test( 24, [ 6,4, 6,2 ] );
Test( 24, [ 6,4, 6,2, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 ] );
Test( 300, [ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ] );
Test( 200, [ 10, 5,5, 10, 6,4, 10, 7,3, 10, 4,6, 10, 3,7, 10, 9,1 ] );
Test( 200, [ 9,1, 10, 5,5, 10, 6,4, 10, 7,3, 10, 4,6, 10, 3,7, 10 ] );


function Test( score, balls ) {
	var game = WScript.CreateObject( "VectorBowling.Game" );
	for ( var ball in balls )
		game.addBall( balls[ ball ] );
	var calculated1 = game.score, calculated2 = game.score;
	if ( calculated1 == score && calculated2 == score )
		WScript.Echo( "Pass" );
	else
		WScript.Echo( "Failed: " + calculated1 + "/" + calculated2 + " not " + score + ": " + balls.toString() );
}


]]></script>
</job></package>