Before starting on writing test cases you need some familiarity with the GNUstep base library, Guile, Gstep-Guile and Greg. I recommend that you get the documentation for all these packages together on your machine and set bookmarks in your favorite web browser to point to it (and to this documentation of course).
The test suite for the gstep-base library consists of the basic framework plus directories containing tests for individual classes.
Mostly you should be able to pick up how to use this stuff simply by looking at the existing tests - pay special attention to what the initialisation file (`begin.grg') is doing.
One thing to note, if you are not familiar with Guile, is the scope of variables - if you want to pass information from one testcase to another you need to declare the variables outside the testcases (using the `define' primitive) -
(define arith-ok #f) ; For passing info between testcases ; ; A testcase to check an instance of numeric addition ; (greg-testcase "One plus One is two" #t (lambda () (if (eq? (+ 1 1 ) 2) (begin (set! arith-ok #t) #t) #f) )) ; ; A testcase to check arithmetic - only supported if we have addition. ; (greg-testcase "X multiplied by 2 is X plus X" #t (lambda () (if arith-ok (eq? (+ 1 1) (* 1 2)) (throw 'unsupported)) ))
Similarly, if you wish to restrict the scope of a variable to a group of testcases, wrap the whole lot in a `begin' -
(begin (define arith-ok #f) ; For passing info between testcases ; ; A testcase to check an instance of numeric addition ; (greg-testcase "One plus One is two" #t (lambda () (if (eq? (+ 1 1 ) 2) (begin (set! arith-ok #t) #t) #f) )) ; ; A testcase to check arithmetic - only supported if we have addition. ; (greg-testcase "X multiplied by 2 is X plus X" #t (lambda () (if arith-ok (eq? (+ 1 1) (* 1 2)) (throw 'unsupported)) )) ) ; The 'arith-ok' variable is no longer in scope.
The initialisation file `begin.grg' is run before any of the tests are started. This file performs basic startup tasks including -
Tests for an individual class are placed in a directory whose name is the
name of the class. Since the test scripts within a directory are normally
executed in alphabetical order, you may want to add a `begin.grg'
to override that default sequence of execution of scripts.
If this has been done, you must modify the `begin.grg' file whenever
you add a new script to the dictionary, so you may prefer to select your
script names so that the default alphabetical ordering causes them to
run in the correct sequence.
By convention we use -
`basic.scm' for a script calling helper procedures
to test that the class exists and conforms to the protocols it should.
`testXX.scm' where `XX' is a number from `00' to `99'
for the other test scripts - so that they will be run in the order
indicated by the numeric parts of their names.
A script file will contain one or more testcases - each of which constitutes a test of a single well defined feature of the `tool' that the script is meant to test. A testcase is always written using the `greg-testcase' procedure.
The testing framework defines various helper procedures (in `begin.grg' and `Protocols.scm') that you should probably use as the basis for a new testsuite for a class.
You should start any testsuite for a class with `test-alloc' to check that basic operations work. Next you should define a few variables for test objects, assign instances of the class to them (inside a testcase), and pass them to the `test-NSObject' procedure to check that they conform to the NSObject protocol. Then, before writing your own specific tests, you might use other procedures to check conformance to other protocols.
The helper procedures defined in `begin.grg' are -
; Test NSString range checking exception. (test-exception "NSString extracting substring with range beyond end of string" NSRangeException (lambda () (define r (NSMakeRange 6 4)) (define s ([] "NSString" stringWithCString: "Hello")) ([] s substringWithRange: r) ; Should raise exception here. #f ; Shouldn't get to this line. ) )
; Test to see if NSString handles basic object creation tasks. (test-alloc "NSString")
; Test to see if NSString conforms to the NSCoding protocol properly (define myString ([] "NSString" stringWithCString: "Hello world.\n")) (test-NSCoding (list myString))
; Test to see if NSString conforms to the NSCopying protocol properly (define testString ([] "NSString" stringWithCString: "Hello")) (test-NSCopying "NSString" "NSMutableString" (list testString) #f #f)
; Test to see if NSString conforms to the NSMutableCopying protocol properly (define testString ([] "NSMutableString" stringWithCString: "Hello")) (test-NSMutableCopying "NSString" "NSMutableString" (list testString))
; Test to see if NSString conforms to the NSObject protocol properly (define myString ([] "NSString" stringWithCString: "Hello\n")) (test-NSObject "NSString" (list myString))
The `greg-testcase' procedure takes three arguments -
The Guile programming language permits the `thunk' to return in four ways -
As there are no other ways in which the `thunk' may be exited, it is impossible for a testcase to produce a result that doesn't fit into the framework (unless your testcase manages either to crash Guile or enter an infinite loop - in which case you won't get any output).
The value returned by the `greg-testcase' procedure is a boolean -
`#t' if the test resulted in an expected pass, `#f' otherwise.
You can use this return value to make the execution of subsequent testcases
dependent on the success of an earlier testcase.
; ; A testcase to check an instance of numeric addition ; (greg-testcase "One plus One is two" #t (lambda () (eq? (+ 1 1 ) 2) )) ; ; The above testcase will generate output - ; 'PASS: One plus One is two' ;
Go to the first, previous, next, last section, table of contents.