Script Library: 1217 scripts
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

Documentation for: run.r


REBOL

Introduction to (R)EBOL (Un)it

Version: 1.1
Date: 15-may-06
Author: Christophe Coussement (COU)
Contact: christophe--at--chrismich--dot--net

Contents:

1. History
2. References
3. Context
4. Test-driven development
4.1 Why should we test ?
4.2 What's a Test ?
4.3 eXtreme Programming
4.4 What do we need to Test ?
4.5 Tests Classification
5. General Principles
6. How to use RUn ?
6.1 TestCase
6.2 Test functions available in RUn
6.3 TestSuite
6.4 Execution Report
7. Case Study: "Mathematica"
7.1 Requirements Definition
7.2 What we need
7.3 Design
7.4 Coding
7.5 Maintenance
8. Test-driven development quick reference guide
8.1 What is a unit test?
8.2 When it is not a unit test?
8.3 Being test-driven
8.4 How do I pick the next test to implement?
8.5 Before writing the next test, ask:
8.6 What should I test?
8.7 What not to test?
8.8 Should I test private methods?
8.9 Things to always do:
8.10 Things to never do:
8.11 What is a to-do list?

1. History

Date

Version

Description

Author

15-may-06

1.1

Add TDD quick reference guide

COU

14-apr-06

1.0

Update doc

COU

17-may-04

0.1

Initial

COU

2. References

[1] REBOL - Relative Expression Based Object Language - http://www.rebol.com

[2] eXtreme Programing - http://www.xprogramming.com/

[3] eXtreme Programing Wiki in French - http://xp-france.net/

[4] eXtreme Programing in French - http://extremeprogramming.free.fr/

[5] Microsoft Computer Dictionary - Fifth Edition

3. Context

The aim of this documentation is to introduce (R)EBOL (Un)it (aka RUn ). RUn is a tool used for testing and debugging of programs written in REBOL [1].

After a brief introduction to the theory of test-driven development we will follow a step-by-step explanation about the creation and the use of tests, just using a simple code editor and REBOL.

4. Test-driven development

4.1 Why should we test ?

We develop a program or an application because we have to address some needs. Those needs can be personal or professional.

A need can be expressed in terms of functionalities which would be provided by the application.

The whole application is composed of several units of code, and each of those units provides a particular functionality.

It's obvious we want to develop a quality code. Some of those qualities could be:

  • functionality: the application must execute the task it was created for;
  • stability: the application must be stable, which in this case means not to "crash" when some weird influencing factors appear.

Testing the application allow us, in a formal way and using well-defined Tests, to check that the application works as a whole, but also that each unit of code is functioning properly.

In this way will a well-designed TestSuite allow us to quickly estimate the impact on the application caused by a punctual change into the code.

4.2 What's a Test ?

The Microsoft Computer Dictionary [5] teach us the definition of a test:

 testvb. To check program correctness by trying out various sequences and input values.

The developer must test any code unit of a program, to insure the clean working of it.

We speak about test coverage to illustrate the amount of code covered by the Tests.

Following the idea, each Test must be replayed to check if no regression appears (generation of new buggy code).

4.3 eXtreme Programming

The problem of Testing is specially addressed into the new development methodology eXtreme Programming (aka XP ), in which it constitute one of the core practices.

As expressed by Ron Jeffries [2], one of its founder :

"Extreme Programming is a discipline of software development based on values of simplicity, communication, feedback, and courage. It works by bringing the whole team together in the presence of simple practices, with enough feedback to enable the team to see where they are and to tune the practices to their unique situation.

Extreme Programming is obsessed with feedback, and in software development, good feedback requires good testing. Top XP teams practice test-driven development, working in very short cycles of adding a test, then making it work. Almost effortlessly, teams produce code with nearly 100 percent test coverage, which is a great step forward in most shops. (If your programmers are already doing even more sophisticated testing, more power to you. Keep it up, it can only help!)

It isn't enough to write tests: you have to run them. Here, too, Extreme Programming is extreme. These programmer tests, or unit tests are all collected together, and every time any programmer releases any code to the repository (and pairs typically release twice a day or more), every single one of the programmer tests must run correctly. One hundred percent, all the time! This means that programmers get immediate feedback on how they're doing. Additionally, these tests provide invaluable support as the software design is improved."

XP states that the Tests must be written at the same time, even before, the functionality to test. This allows to specify the interface of the functionality. If we are facing a bug, then we first write the test which reproduces the bug. When the code is corrected, we test again and it must pass.

4.4 What do we need to Test ?

We need:

  • a programming environment. In our case ... a REBOL evaluator;
  • a functionality to test;
  • a Test framework, which is an environment which allows us to automate the testing procedures. In our case ... RUn.

4.5 Tests Classification

Several Tests exist.

4.5.1 Programmer Tests

Those are the test the programmer designs and uses to verify the whole of a part of the functionality he implemented.

In the following paragraphs, Programmer Tests will be referred as Tests.

4.5.2 Unit Tests or Test Case

Those tests are used to verify the correctness of several modules (classes, functions). Unit Tests or TestCases regroup one of more Tests.

4.5.3 Integration Tests

Integration tests are situated somewhere between TestCases and Client Tests . They are used to verify the interaction between several execution levels within an application, what is called *integration*.

Integration Tests or TestSuite group one or more TestCases or/and one or more TestSuite.

4.5.4 Client Tests

They are also calledb functional tests .

They are used to verify if the application is correctly functioning within its normal execution environment. Those tests are mostly designed by the client, who wants to insure the results he gets are corresponding to what he wants.

Those tests will not be handled into this documentation.

5. General Principles

Given a functionality we have to implement:

  1. imagine a Test to test this functionality;
  2. write this Test;
  3. submit it to RUn . The Test will _fail_ , which is right because the functionality is not yet coded. This allow us to verify that our Test Framework works well;
  4. write the minimal code the function needs to pass the Test;
  5. submit the Test to RUn . If it fails, correct and test again. If it passes , refactor the functionality and test again until you're satisfy by the implementation;
  6. following the same scheme, write other tests down until all aspect of the functionality have been tested;
  7. integrate this TestCase to other. You obtain your TestSuite ;
  8. submit the TestSuite to RUn and refactor until all Tests pass.

Note

We can state that all line of code is justified by a test which didn't pass. In *Test-Driven development* the Test decides the functionality to implement.

6. How to use RUn ?

We load RUn.r into the work memory :

>> do %RUn.r

Now we can submit a TestCase or a TestSuite to the public interface 'run-test.

6.1 TestCase

A TestCase is a simple text file, named as %some_testcase_ident.test as a convention, in which we can find one or more of following functions:

 setup :allows to build a context for the execution of the Tests of the TestCase , as creating a new file if we have to test a functionality of accessing a file. Because of the readability should this function be placed as the first.
 teardown :used to clean the TestCase context, as deleting the files created by the Tests or the Setup function.
 tests :as defined earlier

Exemple

Let's imagine we have to test an function is-integer? which tests if the passed arg is an integer!. This function will return either true or false. Our Tests could be:

assert is-integer? 3     ;--> test pass because the function returns "true"
assert is-integer? 3.2   ;--> test fails because the function returns "false"

If we have to test a function which return something else than true or false , we can use assert-equal . Let's say we have to test the function: 'add-it. This function takes two args of type integer! and returns the sum of it. If we know the args, we can also predict the return, so we can develop a Test :

assert-equal add-it 2 3 5      ;--> test pass, as 2 + 3 = 5
assert-equal add-it 2 3 6      ;--> test fails, as 2 + 3 <> 6

6.2 Test functions available in RUn

Those functions are designed to create test-cases:

6.2.1 assert

USAGE:

ASSERT al_condition

DESCRIPTION:

xUnit Assert function. Display if submitted condition passes RETURN TRUE or FALSE ASSERT is a function value.

ARGUMENTS:

al_condition -- condition to evaluate (Type: any-type)

6.2.2 assert-equal

USAGE:

ASSERT-EQUAL aany_current aany_expected /only

DESCRIPTION:

xUnit AssertEqual Function. Display if or not the two args are of the same value and type. RETURN true or false ASSERT-EQUAL is a function value.

ARGUMENTS:

aany_current -- current result of evaluation (Type: any-type) aany_expected -- expected result of evaluation (Type: any-type)

REFINEMENTS:

/only -- place of the element does not matter

6.2.3 assert-not-equal

USAGE:

ASSERT-NOT-EQUAL aany_current aany_expected /only

DESCRIPTION:

xUnit AssertNotEqual Function. Display if or not the two args are NOT of the same value and type. RETURN true or false ASSERT-NOT-EQUAL is a function value.

ARGUMENTS:

aany_current -- current result of evaluation (Type: any-type) aany_expected -- expected result of evaluation (Type: any-type)

REFINEMENTS:

/only -- place of the element does not matter

6.2.4 assert-error

USAGE:

ASSERT-ERROR ab_code /type-id ab_type-id

DESCRIPTION:

Display if error is triggered RETURN true or false ASSERT-ERROR is a function value.

ARGUMENTS:

ab_code -- block to evaluate (Type: block)

REFINEMENTS:

/type-id -- Specify the type-id of the error ab_type-id -- Type-ID of the error (Type: any)

6.3 TestSuite

A TestSuite will group one or more TestCases for checking the integration of the application.

This TestSuite is a simple text file, named as %some_testsuite_ident.test, and contents a block! named 'test-suite.

This block! will enumerate the different TestCase and/or TestSuite we want to evaluate:

test-suite: [
    %test-case1.test
    %test-case2.test
    %test-suite1.test
]

RUn will automatically distinguish TestCase from TestSuite, based on the existence of 'test-suite in the last.

6.4 Execution Report

After running all the submitted Tests, RUn will produce an execution report.

For each Test, we will find:

  • the test name
  • if it passed, failed or triggered an error
  • the execution time if it passed

At the end of the report, the amounts of Tests, failures, successes and errors are mentioned.

Note

The actual version of RUn does not have any GUI

7. Case Study: "Mathematica"

7.1 Requirements Definition

Our client, a math teacher, needs a simple program which would allow him to illustrate the use of the four base operations.

This program must allow the use of "+", "-", "/" and "*" on integers and decimals.

7.2 What we need

  • REBOL/Core installed
  • your favorite code editor (notepad is enough for the die-harts)
  • the script RUn.r

7.3 Design

We will isolate "+" and "-" from "*" and "/".

Do do so, we will use the REBOL object!.

The object! 'add-sous will contain the operations "+" and "-" and the object! 'mult-div the operations "*" and "/".

Each operation will be coded by mean of a function.

Each function will be verified by a Test. An object! will be checked by a TestCase. The integration of the two objects by a TestSuite.

7.4 Coding

7.4.1 Preparation

Open a new file in our editor.

Type the REBOL header and fill the needed informations:

rebol [
    title: "Mathematica"
    purpose: "use the four base operations"
]

and create our first 'context:

rebol [
    title: "Mathematica"
    purpose: "use the four base operations"
]    
add-sous: context []

Let's save our file and name it: mathematica.r

7.4.2 Test of the function 'addition

We will now create our first TestCase. Open a new file and type:

test-add-1: does [
    assert-equal add-sous/addition 1 2 3
]

Important

Please remark all the tests, setup and teardown are defined as functions without any arguments or locals. The best way to achieve this is making use of the mezzanine 'does.

Save this file under the name: mathematica-addition.test.

We will try to verify of the 'addition function evaluation result when passing the parameters 1 and 2 is 3, as expected.

Launch the REBOL console (rebol.exe for the the Windows users), and load RUn.r:

>> do %RUn.r
Script: "(R)EBOL (Un)it" (14-Apr-2006)

We are now ready to submit our first Test to the 'run-test function of RUn :

==================
= (R)EBOL-(Un)it =
= Test Framework =
=     v.1.9      =
==================    
== OUTPUT ===============================================      
> mathématica-addition.test    
 >> SETUP
     --> Done
 >> test-add-1
 !-> ERROR generated:
     Script Error: add-sous has no value
     Near : assert-equal add-sous/addition 1 2 3
     Where: test-add-1    
== TEST REPORT ==========================================    
TOTAL : 1
 => Passed   : 0
 => Failures : 0
 => Errors   : 1    
=========================================================    
End of test    
Press [ENTER] to Quit

Our Test has generated an error, which is displayed by RUn . Indeed: we didn't already load the script mathematica.r into memory !

Although often qualified by the methodlogy cons as "voodoo practices", this test MUST be executed, and the expected result MUST be an error!.

If no error! should be triggered when we were expecting one, we could indeed not conclude anything about the validity of our code !

Let's modify our TestCase to load mathematica.r, into the setup :

setup: does [
    do %mathematica.r
]
test-add-1: does [
    assert-equal add-sous/addition 1 2 3
]

and submit our Test to the function 'run-test :

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
> mathématica-addition.test    
    >> SETUP
        --> Done
    >> test-add-1
        !-> ERROR generated:
            Script Error: Invalid path value: addition
            Near : assert-equal add-sous/addition 1 2 3
            Where: test-add-1       
== TEST REPORT ==========================================    
 TOTAL : 1
    => Passed   : 0
    => Failures : 0
    => Errors   : 1    
=========================================================    
End of test    
Press [ENTER] to Quit

As we can see does RUn mention us the correct execution of the Setup .

Again, the generated error! is here normal, as we didn't code anything yet into our context 'add-sous ...

The next step is to create the minimal necessary code for the function can be evaluated by the Test. Edit the mathematica.r script, add the arguments and force the return to 3 :

rebol [
    title: "Mathematica"
    purpose: "use the four base operations"
]    
add-sous: context [
    addition: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][3]
]

Re-run our Test :

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
> mathématica-addition.test    
    >> SETUP
        Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 1
    => Passed   : 1
    => Failures : 0
    => Errors   : 0 
=========================================================    
End of test    
Press [ENTER] to Quit

Done! Our first Test is now usable :-) We can begin to work on the implementation of the functionality.

Let's edit mathematica.r and add the desired functionality:

rebol [
    title: "Mathematica"
    purpose: "use the four base operations"
]     
add-sous: context [
    addition: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][
        return arg1 + arg2
    ]
]

And test:

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
> mathématica-addition.test    
    >> SETUP
        Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 1
    => Passed   : 1
    => Failures : 0
    => Errors   : 0 
=========================================================    
End of test    
Press [ENTER] to Quit

Test passed ! Now we can focus on other Tests for submitting other values to our function:

setup: do [
    do %mathematica.r
]
test-add-1: does [
    assert-equal add-sous/addition 1 2 3
]
test-add-2: does [
    assert-equal add-sous/addition 0 0 0
]
test-add-3: does [
    assert-equal add-sous/addition -1 1 0
]
test-add-4: does [
    assert-equal add-sous/addition 1.2 0.3 1.5
]

Of course, it is recommended to submit our function to each Test, as they are created.

After the fourth Test we got:

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
    >> SETUP
Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
    >> test-add-2
        --> Passed in <0:00>
    >> test-add-3
        --> Passed in <0:00>
    >> test-add-4
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 4
    => Passed   : 4
    => Failures : 0
    => Errors   : 0
=========================================================  
End of test    
Press [ENTER] to Quit

Until now, our function does behave as expected. Let's introduce a fifth Test which will generate an error:

(...)
test-add-5: does [
    assert-equal add-sous/addition 1.2 $0.3 1.5
]

And evaluate the result:

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
    >> SETUP
Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
    >> test-add-2
        --> Passed in <0:00>
    >> test-add-3
        --> Passed in <0:00>
    >> test-add-4
        --> Passed in <0:00>
    >> test-add-5
        !-> ERROR generated:
            Script Error: addition expected membre2 argument of type: integer decimal
            Near : assert-equal add-sous/addition 1.2 $0.30 1.5
            Where: test-add-5
== TEST REPORT ==========================================
 TOTAL : 4
    => Passed   : 4
    => Failures : 0
    => Errors   : 0
=========================================================  
End of test    
Press [ENTER] to Quit

Getting an error here indicates our function works well, because an error was expected !

But as we were expecting an error, we could use the functionnality provided by RUn to test the throwing of the error:

(...)
test-add-5: does [
    assert-error [add-sous/addition 1.2 $0.3 1.5]
]

Please note the code submitted to 'assert-error MUST be put into block!, for proper evaluation by RUn.

which results in:

>> run-test %mathematica-addition.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
    >> SETUP
Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
    >> test-add-2
        --> Passed in <0:00>
    >> test-add-3
        --> Passed in <0:00>
    >> test-add-4
        --> Passed in <0:00>
    >> test-add-5
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 4
    => Passed   : 5
    => Failures : 0
    => Errors   : 0
=========================================================  
End of test    
Press [ENTER] to Quit

7.4.3 Test of the function 'substraction

We will follow the same procedure as for the function 'addition.

  • Create un file "mathematica-substraction.test"
  • Write a write which will fail
  • Implement the minimal functionalities to pass the test
  • Refactor the function to address our needs
  • Implement other tests

Our TestCase should now look like this:

setup: do [
    do %mathematica.r
]
test-sous-1: does [
    assert-equal add-sous/substraction 5 8 -3
]
test-sous-2: does [
    assert-equal add-sous/substraction 0 0 0
]
test-sous-3: does [
    assert-equal add-sous/substraction -1 1 -2
]
test-sous-4: does [
    assert-equal add-sous/substraction 1.2 0.3 0.9
]
test-sous-5: does [
    assert-error [add-sous/substraction 1.2 $0.3 1.5]
]

and the script mathematica.r :

rebol [
     title: "Mathematica"
   purpose: "use the four base operations"
]     
add-sous: context [
    addition: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][
        return arg1 + arg2
    ]
    substraction: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][
        return arg1 - arg2
    ]
]

and our Test works !

>> run-test %mathematica-substraction.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================    
== OUTPUT ===============================================      
    >> SETUP
Script: "Mathématica" (none)
        --> Done
    >> test-add-1
        --> Passed in <0:00>
    >> test-add-2
        --> Passed in <0:00>
    >> test-add-3
        --> Passed in <0:00>
    >> test-add-4
        --> Passed in <0:00>
    >> test-add-5
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 4
    => Passed   : 5
    => Failures : 0
    => Errors   : 0
=========================================================  
End of test    
Press [ENTER] to Quit

7.4.4 Integration of 'addition and de 'substraction

As we which to use those two functionalities together, we have to integrate them, of, use REBOL terms, use them within a same context.

Let's create a TestSuite, which is a file we will name mathematica-suite.test.

This will contain a block! , named 'test-suite , where the file names of the TestCase will be mentioned:

test-suite: [
    %mathematica-addition.test
    %mathematica-substraction.test
]

Now we submit our TestSuite to the evaluation of 'run-test :

>> run-test %mathématica-suite.test

==================
= (R)EBOL-(Un)it =
= Test Framework =
=     v.1.9      =
==================
== OUTPUT ===============================================
> mathématica-addition.test
 >> SETUP
Script: "Mathématica" (none)
     --> Done
 >> test-add-1
     --> Passed in <0:00>
 >> test-add-2
     --> Passed in <0:00>
 >> test-add-3
     --> Passed in <0:00>
 >> test-add-4
     --> Passed in <0:00>
 >> test-add-5
     --> Passed in <0:00>
Script: "Mathématica" (none)
> mathématica-soustraction.test
    >> SETUP
        --> Done
    >> test-sous-1
        --> Passed in <0:00>
    >> test-sous-2
        --> Passed in <0:00>
    >> test-sous-3
        --> Passed in <0:00>
    >> test-sous-4
        --> Passed in <0:00>
    >> test-sous-5
        --> Passed in <0:00>
== TEST REPORT ==========================================
 TOTAL : 10
    => Passed   : 10
    => Failures : 0
    => Errors   : 0
=========================================================
End of test
Press [ENTER] to Quit

Integration passed !

7.4.5 Tests and integration of functions 'division and 'multiplication

We will use the same approach than this of the two first functions.

The implementation of it will be left as an exercise for the motivated reader :-)

7.5 Maintenance

When our Tests work for 100%, which is our objective, we can be as sure as possible of the quality of our application.

One of the main advantage of this procedure is how easier maintenance becomes: the side effects of the most little change into the code are directly intercepted by our TestCases.

If, involuntary, we would modify our script this way:

rebol [
     title: "Mathematica"
   purpose: "use the four base operations"
]   
add-sous: context [
    addition: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][
        return arg1 + arg ;<== !!! should be 'arg2 !!!
    ]
    substraction: func [
        arg1 [integer! decimal!]
        arg2 [integer! decimal!]
    ][
        return arg1 - arg2
    ]
]

Submitting our application to the framework gives:

>> run-test %mathematica-suite.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================
== OUTPUT ===============================================
> mathematica-addition.test
    >> SETUP
        --> Done
    >> test-add-1
        !-> ERROR generated:
            Code : 300
            Type : script
            ID   : no-value
            Arg1 : arg
            Arg2 : none
            Arg3 : none
            Near : return arg1 + arg
            Where: addition
    >> test-add-2
        !-> ERROR generated:
            Code : 300
            Type : script
            ID   : no-value
            Arg1 : arg
            Arg2 : none
            Arg3 : none
            Near : return arg1 + arg
            Where: addition
    >> test-add-3
        !-> ERROR generated:
            Code : 300
            Type : script
            ID   : no-value
            Arg1 : arg
            Arg2 : none
            Arg3 : none
            Near : return arg1 + arg
            Where: addition
etc...

As stated, the typo is targeted and mentionned !

If we would want to "improve" our functions and we would modify 'addition this way :

rebol [
    title: "Mathematica"
    purpose: "use the four base operations"
 ]    
 add-sous: context [
     addition: func [
         arg1 [integer! decimal!]
         arg2 [integer! decimal!]
     ][
         return arg1 + arg2 + 1 ;<== !!! Oups :-( !!!
     ]
     substraction: func [
         arg1 [integer! decimal!]
         arg2 [integer! decimal!]
     ][
         return arg1 - arg2
     ]
 ]

Our Test should give:

>> run-test %mathematica-suite.test
   ==================
   = (R)EBOL-(Un)it =
   = Test Framework =
   =     v.1.9      =
   ==================
== OUTPUT ===============================================
> mathematica-addition.test
    >> SETUP
        --> Done
    >> test-add-1
        *-> Data expected: 3 of type: [integer]
            but was : 4 of type: [integer]
    >> test-add-2
        *-> Data expected: 0 of type: [integer]
            but was : 1 of type: [integer]
    >> test-add-3
        *-> Data expected: 0 of type: [integer]
            but was : 1 of type: [integer]
    >> test-add-4
        *-> Data expected: 1.5 of type: [decimal]
            but was : 2.5 of type: [decimal]
    etc...

As the introduced change implicates the function does not anymore behave as expected.

Have fun !

8. Test-driven development quick reference guide

We reproduce here the quick reference guide, from http://www.testdriven.com/files/doshi/TestDrivenDevelopmentReferenceGuide.pdf, Copyright 2005-06 Instrumental Services Inc, which contains many usefull elements.

8.1 What is a unit test?

A "unit test" tests very specific, much focused, single aspect of functionality. Characteristics of a unit test:

  • Focused, making only one claim at a time.
  • Fast to run.
  • Independent of
    • Each other.
    • The environment.
    • The order in which it is run.
  • Same high quality as the production code.
  • Automatic.

8.2 When it is not a unit test?

A test that does not operate in isolation is not a unit test. It is safe to assume that a test that connects to the network or a database or a real file is not a unit test. Use mock objects or stubs to test in isolation.

8.3 Being test-driven

  • Being test-driven is not about testing, but about evolving the design to meet the requirements.
  • Each unit test corresponds to a single requirement that the code must satisfy. Test-Driven Cycle:
  • Prepare a list of test cases.
  • Follow the TDD Rhythm:
    • Pick a test to implement.
    • Write a failing test.
    • Quickly, make the test green.
    • Refactor to eliminate duplication.

8.4 How do I pick the next test to implement?

Pick a test that

  • You are confident that you can implement.
  • You know is the next logical step and will give you the most learning.

8.5 Before writing the next test, ask:

  • Is there any duplication? DO I need to refactor first?
  • What are the correct inputs? How am I going to check for the effect of these inputs?
  • Where does the operation belong?

8.6 What should I test?

Test everything that could possibly break.

8.7 What not to test?

  • DO not test getters and setters.
  • It is almost impossible to unit test the GUI, so keep the GUI as thin as possible.

8.8 Should I test private methods?

Testing the public interface of a class should be sufficient. Public interface should invoke the private methods

8.9 Things to always do:

  • Write test first.
  • Maintain a to-do list.
  • Write new code, only if, there is a failing automated unit test.
  • Run all the tests all the time and not just a single isolated test.
  • Keep the test code of the highest quality without any code smells.

8.10 Things to never do:

  • Write multiple failing tests at a time.
  • Write new test without eliminating duplication.
  • Commit code with failing tests.

8.11 What is a to-do list?

  • To-do list contains
    • Examples of test cases.
    • Any refactoring that needs to be done.
  • To-do list should be kept current. At any time, it should tell the following:
    • Current test or refactoring being carried out.
    • Pending tests.
    • Pending refactorings.
MakeDoc2 by REBOL- 15-May-2006