CardFRP Unit Testing

Introduction

The unit testing for the CardFRP component is intended to exercise all non-trivial methods in the basic CardFRP classes (GameObject, GameActor, GameContext, GameAction, and Dice). This testing will be broken into two parts:

In addition to the above pass/fail test cases, we will also provide sample code to exercise a wide range of common game scenarios (e.g. searches, interactions, combat, scrolls).

Per-Class Unit Testing

GameObject Unit Testing

The most interesting code in the GameObject class is the possible_actions() and accept_action() methods. The test cases to exercise them are combinations of situations to exercise different paths through the code ... and so should be regarded as white box test cases.

The possible_actions() functionality to be exercised is: A set of (specification based, black-box) test cases to exercise this functionality would be:

The accept_action() method in this base class only knows how to handle attribute-changing verbs. ATTACK verbs are handled by the GameActor sub-class. The functionality to be exercised is:

There is a little complexity to these rules, and constructing a set of test cases (based on an understanding of these requirements and their interactions) to well-exercise their correct implementation might reasonably be considered to cross the line into white-box testing:

The load() method, which draws on other classes, will be exercised (for both GameActors and GameContexts in the game PyTest tests.

GameAction Unit Testing

The interesting code is in the act() method, where we recognize and split up compound verbs, decide which (object and initiator, (base and sub-type) ACCURACY, DAMAGE, POWER and STACKS attributes apply to each, compute TO_HIT and TOTAL attributes, and pass these to the recipient's accept_action() method.

The functionality to be exercised is:

Again, the construction of a set of test cases to exercise the correct handling of compound verbs might reasonably be considered to cross the line into white box testing:

In support of the last two, we will create a test-recipient, who's accept_action method simply returns (as status) the verb and attribute values. The test functions can parse this output to confirm that the received GameAction had the expected verb, TO_HIT and TOTAL attributes, in the correct order.

The test cases to exercise this are combinations of situations to exercise different paths through the code ... and so should be regarded as white box test cases.

Dice Unit Testing

The Dice class is simple and independent, and so should be very easy to test. There are two obvious types of tests:

The easiest way to test correct rolling is to do many more rolls than the range width, and ensure that all returned rolls are within the expected range. I will write a routine:
    test(
	 string formula,	# dice roll formula
	 int min_expected,	# lowest legal return value
	 int max_expected,	# highest legal return value
	 int num_rolls)		# number of rolls to test
that will do this, and use it to test each of the basic types of roll formula:
formula min expected max expected num rolls
"3D4" 3 12 40
"d20" 1 20 80
"D%" 1 100 300
"2D2+3" 5 7 7
"3-9" 3 9 9
"47" 47 47 10
47 47 47 10
"-3" -3 -3 10

The obvious formula errors are typos and missing values:

The above are merely obvious examples the supported expression formats and obvious errors. As such, these would probably be considered to be black box test cases.

GameActor Unit Testing

By far, the most interesting code in GameActor is the ATTACK handling in the accept_action() method. The obvious things to check are:

The simplest tests of attribute comparison and LIFE update might be: Again, while the above break-down of cases could be done on the basis of the specifications, it represents deeper and more complete analysis "make sure each function does what it is specified to do". As such, this too might reasonably be considered to be a set of black box test cases.

Non-ATTACK (condition affecting) actions are not implemented in this class, but forwarded to our underlying GameObject super-class. Even though those actions are not actually processed in this class, we should still confirm that they are correctly forwarded and have the expected effects on our attributes, based on our RESISTANCEs:

We can test the use of dice rolls:

GameContext Unit Testing

The GameContext implementation is, for the most part, a trivial extension of the base GameObject class, and is easily exercised with a few black-box tests:

Whole Component Test Scenarios

Some of the obvious unit test cases involve the interactions of multiple classes, and so I have chosen to put these into the (PyTest) test_cardfrp.py module. These include:

In addition to these "does it work" test cases, there is a scenarios.py that provides demonstrations of fully integrated functionality: