GeneralFixture
fixture: com.jbergin.GeneralFixture

Back to the [FrontPage] [FixtureDevelopment page]

Copyright (C) 2004, Joseph Bergin. All rights reserved

Last modified by true on May 17, 2006 at 09:38:37 AM

If you have comments or suggestions, please leave them here: ^CommentsAndSuggestions. I'd also like to know if you are using this.



General Concepts

The problem. Suppose you are writing an application and you want to write a bit and test a bit to get it right.

Alternatively, suppose you are an instructor who wants to give your students an executable specification of a program that they can use to get their code right.

Again, suppose you are an XP team building an application and you want an additional aid in writing customer acceptance tests that can be written before the application is finished (or even begun) and executable as you go along, to tell you about your progress.

JUnit (http://junit.org) handles some of these problems. Fit (http://fit.c2.com) and Fitnesse (http://fitnesse.org) also work together to solve them. This works with all of these in a way that may be more convenient in some situations.


GeneralFixture is a means both of scripting program actions and specifying the behavior of programs before they are built. You can create objects, send messages to objects and make assertions about their behavior. You can exercise individual classes. You don't need a main routine at all, as this system provides a fine grained execution engine. Nor do you need I/O statements to see the effect of your code, though output will be captured and is available (in the Error Log).


The tests are in the form of html tables. In this system, tables are entered using a simplified syntax. http://fitnesse.org/FitNesse.UserGuide.

If a page has a test button at the left (you can turn this on or off with the properties button), then pushing the test button will execute all the code described on the page (it can take a minute or two). If a test "succeeds" its cell will be shown in green. If it fails, in red. Queries are shown in purple. If an exception is thrown by your code you will see it in a yellow cell. Some failures cause other tests to be ignored. Any textual output that your tests produce will be shown on the error log page you can reach from the bottom of the test results page.

Note that there are some limitations here. First is that it is not a graphical system and has no way to show graphical output. More important, there are some programs you can write that can't be tested here. This has not (yet) been tested with threaded programs. There are some parameter types that will not work with this system, but int, double, String, and object types should work ok.

Note also, that what you do in one table will carry over to the next. So if you save some abbreviations in the first table, they will still be in place for the rest of the page.

At any given time there is one "current" class. The create command will create an object in this class. Likewise there will be (at most) one current Tester class. Assert commands refer to this class. The default assert class is null, so any asserts will fail.

Here we will exercise clases defined in the KarelJRobot.jar, though any class will do. The Java Docs will show you what commands can be sent: http://csis.pace.edu/~bergin/KarelJava2ed/KJRdocs/

com.jbergin.GeneralFixture
kareltherobot.Directions
Static Field North
Static Field East
Static Field South
Static Field West
Static Field infinity
Class kareltherobot.World world
Static setDelay 1
Static placeBeepers 1 1 3  
Tester kareltherobot.KJRTest local
Assert BeepersInWorld 3
Class kareltherobot.Robot Robots
Create karel 1 1 North 0
Static pickBeeper
Assert NoBeepersInBeeperBag karel
Message karel pickBeeper
Message karel anyBeepersInBeeperBag  
Assert BeepersInBeeperBag karel

Setup

You will need to establish a classpath here with the !path widget. You will need jbfixture.jar and any classes of your own that you would like to test. They can be in jar files or in class directories.

Get the necessary files

You can get the needed Jar files from the following links:
http://csis.pace.edu:8077/files/JoeBergin/Fixtures/junit.jar
http://csis.pace.edu:8077/files/JoeBergin/Fixtures/jbfixture.jar

You need the above in addition to the fitNesse.jar available from http://fitnesse.org

Form of the Tables


Row 1

The first row always gives the name of the testing fixture. Here it is com.jbergin.GeneralFixture. After the first table you can abbreviate this to GeneralFixture.

Row 2

The second row is the full name of some class. The system is quite general and most classes are usable here. For a specialized version, see KarelFixture. You can optionally put a second cell on this row with a name that you want to use within this system for this class. Think of it as a local abbreviation. Note however, that you will be naming several things here, including objects, and your names need to be distinct. If you define a name again, its latest definition prevails.

This command establishes the current class. Until it is changed, the Create command will create objects in this class. If you name a class in one table you can use the abbreviation instead of the full name later, including in other tables.

By default the parameter interpretation for methods and constructors in this class will be set to int and double. This means that when you use a parameter in a Create command (for example) that consists of just digits, the system will assume it is either an int or an Integer. If the corresponding constructor (or method, in the general case) requires long, for example, you can put extra cells on the second row after the name cell. In these cells you can use any of byte, short, int, long, float, double. The system will then change its behavior to assume your parameters take on the given types. Note, that float and double only apply to values with a decimal point and the others only to those without. Once you set the system it will remember what you say until you change it by giving a different set in a new table, or equivalently in a Class command. If you put several such cells that conflict, such as |int|long|byte| then only the last one will be in effect, but you can set the defalults differently for things that look like integer data and things that look like floating point data. Note that the system purposly confuses the primitive types and the wrappers, so |long| works for both long and Long.

Rows 3 ... n

The rest of the rows in a table conform to the rules of any of the following commands in any order. For these rows, the first cell is the name of the command (always capitalized and if it has two words, a single space between the words). The rest of a row is determined by the individual command's requirements. There is one cell for each parameter. Most commands have a variable number of parameters.

Commands


Create (create a new object in the current class)

parameters (1 or more usually, depending on the class)
(1) the name for the object you want to create
(2...) the required parameters for some constructor for the current class, one value per cell.

See examples above. When you write your own classes, the structure must adhere to that of the constructors you write. Usually they are as above.

See the note on parameters at the end.

Message (send a message to an object)

parameters (2 or more, depending on the message to be sent: one additional for each parameter of the message)
(1) the name of the object to receive the message
(2) the message name itself
(3...) any parameters that the message requires. These can be ints, booleans, Strings, or objects only). Some methods have no parameters.
(optional) At the end of the list of parameters you can put an optional empty cell. If you do this the result of sending the message will be put into this cell when you run the test. A message for a void method returns nothing, so the cell is filled with the word: null. If a message does return some information some String representation will be placed here. The cell is colored a shade of purple, indicating a query.
(optional) If you place a query cell (empty) you may follow it with another cell with a string value (an abbreviation). The value returned from the message will be saved with this name for later use. See Static for an example of use.

com.jbergin.GeneralFixture
kareltherobot.StairClimber Climbers
Create karel 1 1 East 0
Message karel getBeeper

com.jbergin.GeneralFixture
kareltherobot.Robot Robots
Create jane 1 1 North 3
Message jane anyBeepersInBeeperBag  

Query Message (send a message to an object and optionally make assertions about the result)

parameters (3 or more, depending on the message)
(1) The name of an object to receive the message
(2) The name of the message to send
(3) The query/assertion cell. If this cell is empty a query is being made and the results of sending the message will be shown here. If you put a value here you are asserting that the returned value from the message will be what you write.
(4 ...) Any parameters required by the message.

Note the difference between Message and Query Message. In Message, the query cell comes last and must be empty. In Query Message the query/assert cell comes immediately after the message name, but before any parameters. You cannot make assertions with a Message command.

GeneralFixture
Robots
Create karel 1 1 North 0
Query Message karel move null
Query Message karel anyBeepersInBeeperBag true
Query Message karel anyBeepersInBeeperBag  

Show Methods (display which method is called for each message/ query message)

parameters: none

This command will toggle between showing and not showing the method that is executed whenever you send an object a message or query message. This is primarily for debugging this system to check that the Java reflection is picking out the right method.

Class (change the current focus class to create objects in the new class)

parameters (1 or 2)
(1) The fully qualified class name you want to use, or the abbreviation of some class you saved earlier
(2 optional) The abbreviation (name) you want to save for this class.
(3... optional) Just as in the second row of a table, you can change the default parameter behavior in any Class command. Note that it changes the behavior for the system, not just for this class. You can give extra cells after the name cell setting the looked for parameter types to byte, short, int, and long, for integer types and to float or double for floating types. You can actually put several cells but only the last of each type will be effective.

GeneralFixture
kareltherobot.Robot robot
Create jane 1 1 North 0
Class kareltherobot.UrRobot ur int float
Create karel 1 1 North 0

Here jane will be a kareltherobot.Robot and karel will be a kareltherobot.UrRobot?. We also save the name ur as an abbreviation to use in future tables or Class commands.

Note that the second row of every table is like an implicit Class command.

Static (execute static methods defined in the current class)

parameters (1 or more)
(1) the name of the message to send to the current class
(2...) any required parameters of the message.
(optional) You may place an empty cell after all the parameters. If you do so, any result of the message will be placed here (query).
(optional) If you place a query cell (empty) you may follow it with another cell with a string value (an abbreviation). The value returned from the message will be saved with this name for later use.

The Java Docs will show you what commands can be sent: http://csis.pace.edu/~bergin/KarelJava2ed/KJRdocs/. See the World class there.

GeneralFixture
kareltherobot.World
Static reset
Static placeBeepers 2 2 1
Static delay   theDelay
Class Robots
Create jj theDelay 1 North 0
Assert At jj 1 1

Place a beeper on second street and second avenue after clearing out everything in the world. It also shows the current delay. Finally it shows a somewhat nonsensical use of a returned value.

Field (retrieve a public instance field of an accessible object) -- NEW No test here yet

parameters (3)
(1) the name of an object
(2) the name of some public field of the object
(3) a local name that you want to use here to refer to this field.

The following gives the form only. The test will fail as there is no such field in the object.
GeneralFixture
kareltherobot.Robot robot
Create karel 1 1 North 0
Field karel size itsSize

This is intended to retrieve object valued fields so that you can send them messages. You cannot change the value of the field here.

Static Field (retrieve static fields of a class an make them available here as parameters)

parameter: the name of a field defined in the current class

The field will be retrieved and saved here with the same name.

com.jbergin.GeneralFixture
kareltherobot.Directions
Static Field North
Static Field East
Static Field South
Static Field West
Static Field infinity

This initialization needs to be done for the classes exercised here as every robot construction requires a direction object. This only needs to be done once. In general you can retrieve and save any named values in this way.

Assert (make assertions from a JUnit test class -- default is null)

parameters (1 or more depending on the assertion)
(1) the name of the assert method from the current tester class (default is kareltherobot.KJRTest)) without the initial assert prefix, which will be automatically added. All of these, then, begin with an upper-case letter.
(2...) any parameters required by the assertion.

If the assertion succeeds, the name cell will be shown green, if it fails, red. See the KJRTest class in the Java Docs: http://csis.pace.edu/~bergin/KarelJava2ed/KJRdocs/. You may change the assertion tester class with the Tester command.

GeneralFixture
world
Static reset
Tester kareltherobot.KJRTest
Assert BeepersInWorld 0
Static placeBeepers 1 1 3
Assert BeepersInWorld 3
Assert BeepersInWorld 0
Class Robots
Create karel 1 1 North 0
Assert RobotsInWorld 1
Assert RobotsAt 1 1

One of the above assertions is intended to fail.

Note that you can't execute tests from these classes with this command, but only assertions.

Tester (change the class used for defining assertions)

parameters(1 or 2)
(1) the name of the class used to make assertions. The default is null. You may use any class that extends junit.framework.TestCase.
(2 optional) an optional name for this class for use in future Tester commands in case you need to go back and forth.

You name the class. An instance of it will be created and used by the Assert command. Note that its setUp method is NOT called. You can, however, open the class with the Class command and manipulate it with Message. Be aware that you should call setUp yourself between the test methods of the class as there is no infrastructure here to do it automatically.

GeneralFixture
kareltherobot.StairClimber
Tester kareltherobot.KJRTest local

Set KJRTest as the current tester and executer of Assert messages and give it the name local.


Note on parameters

This system works by looking at the values you put in the cells and trying to infer from them which method or constructor to use. This is a difficult process and is not perfect, since values of different types may look the same. In particular, the system can't distinguish by itself whether 42 is an int or a long or whatever. Because of this, it makes assumptions, but you are allowed to override the assumptions in two ways. The system also does not distinguish between primitive types and their wrappers, so true serves as a valid value for either a boolean or a Boolean parameter. The system automatically handles boolean, String, and other object types. The integer and floating types present a difficulty. For this reason, the system uses default values for each kind, with int and double being the original defaults. You can change these in the second row of any table and also in any Class command as described above. This is enough, provided that you don't have any methods that take, for example, both an int and a long. In those cases you can override the assumptions in a second way.

Within any numeric cell, you can proceed the value you want to use with a type indicator. The valid values are
\byte
\short
\int
\long
\float
\double
So, for example, if the default is the original int and you want to send a message that requires a long argument you can say something like |Message|doIt|\long 5|
If there is a method that has name doIt and requires a long paramenter, it will be found.
In the rare situation in which you want the cell to be interpreted as a String that begins with one of our type tags, you can preceed that with \String and the rest will be interpreted as a String. \String will also force a string of digits (which looks like an int) to be interpreted as a String.

The system also assumes that things that don't otherwise parse as boolean, numeric, or known object types are Strings. Sometimes you want them to be char (or Character) instead. In this case you can preceed a single character with the \char type tag so that the system looks for a method or constructor with this type parameter. You might also want a digit to be interpreted as a character. Following \char you may put the numeric equivalent of a char, a character in apostrophes, or a string. In the latter case the first character will be used.


GeneralFixture
kareltherobot.StairClimber sc long
Create karel \int 1 \int 1 East \int 0

fit.Summary


Additional Examples of Use


^TestCalculator

^TicketMachine: an example from Barnes and Kolling's BlueJ book

Limitations

This system has some limitations as well. Most important is that it is not suited to graphical interface programs. It has no way to show you a Java Frame, for example, either in an application or applet form. The code here runs on the server, not the client.

It has not been tested with and may not run well on multi-threaded code.

[ User Guide] [.FrontPage] [.RecentChanges]