~budgester/irm/trunk

« back to all changes in this revision

Viewing changes to testing/README

  • Committer: budgester at budgester
  • Date: 2008-03-05 23:14:13 UTC
  • Revision ID: budgester@budgester.com-20080305231413-k5vqfuckfo09ju42
Initial import of IRM codebase

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Unit Testing for IRM
 
2
======================
 
3
 
 
4
Unit testing is the process of writing lots of little pieces of test code,
 
5
each of which test some part of the functionality of a software system.  By
 
6
having a comprehensive set of test cases which are satisfied by the
 
7
production code, we can have real confidence in our software, instead of
 
8
just "hoping" it works.
 
9
 
 
10
Prerequisites
 
11
---------------
 
12
 
 
13
The test suite makes a few assumptions about your environment.  If these
 
14
conditions aren't met, the chances are that the testsuite won't report
 
15
accurately.
 
16
 
 
17
* A MySQL server, listening on the localhost, with a database called 'test',
 
18
  a user called 'test' with full rights to the 'test' database, with no
 
19
  password.
 
20
 
 
21
* A HTTP server listening on the localhost.  Obviously, the webserver must
 
22
  be configured to run PHP scripts...
 
23
  
 
24
  If you're running the tests from the command line, the webserver must be
 
25
  set up to serve requests for /_irmtest/ to the directory containing your
 
26
  IRM codebase.  There is also an experimental HTML-based test runner, which
 
27
  operates through a web browser.  It's much prettier, but I don't guarantee
 
28
  that it'll run all the tests quite properly.
 
29
 
 
30
* To run the tests in their text mode, you must have a command-line PHP
 
31
  interpreter.
 
32
 
 
33
If you're interested in enhancing the test suite by autodetecting the
 
34
presence of these resources and only running tests for which the necessary
 
35
resources are available, please do!
 
36
 
 
37
What is a unit test?
 
38
----------------------
 
39
 
 
40
The general structure of all unit tests is as follows:
 
41
 
 
42
* Setup pre-conditions (the state of the system prior to the execution of the
 
43
        code under test)
 
44
* Run the code to be tested, giving it all applicable arguments
 
45
* Validate that the code ran correctly by examining the state of the system
 
46
        after the code under test has run, and ensuring that it has done
 
47
        what it should have done.
 
48
 
 
49
The pre-conditions are normally either set at the beginning of the test
 
50
method, or in a general method called setUp() (see below for the structure
 
51
of test code).
 
52
 
 
53
Running the code itself is normally making a call to the function to
 
54
be tested, constructing an object, or, for Web applications, making one or
 
55
more HTTP requests to the webpage of interest.
 
56
 
 
57
Validating the post-run state of the system is done by examining the
 
58
database, system files, and script output, and using various assert methods
 
59
to communicate the success or failure of various tests to the user.
 
60
 
 
61
So how do I write a Unit Test?
 
62
-------------------------------
 
63
 
 
64
Put together a snippet of code which sets up the state of the application,
 
65
then run the code to be tested, and check the result.  This snippet of code
 
66
should be placed in a test case in a method named test[Something]().  Each
 
67
test method takes no arguments and returns nothing.
 
68
 
 
69
Have a look at the existing test code in the testing/ directory of the
 
70
IRM source distribution for examples of how to write test code.
 
71
 
 
72
When you create a new test class, you need to tell the test code runner that
 
73
it needs to run the new test code.  Simply add a new line to alltests like
 
74
the existing addTestFile calls.
 
75
 
 
76
What should I test?
 
77
---------------------
 
78
 
 
79
The naive answer would be "everything".  However, that is impractical. 
 
80
There are just too many things that could be tested for a policy of "test
 
81
everything" to allow any actual code to be written.
 
82
 
 
83
It helps to think of tests as pulls of the handle on a poker machine.  Each
 
84
pull costs you something (some time to write it).  You "win" when a test
 
85
that you expected to pass fails, or when a test you expected to fail
 
86
actually passes.  You lose when a test gives you no additional useful
 
87
feedback.  You want to maximise your winnings.
 
88
 
 
89
So, write tests that demonstrate something new and different about the
 
90
operation of the system.  Before you write any production code, write a test
 
91
which defines how you want the system to act in order to pass the test.  Run
 
92
the test suite, verify that the test fails.  Now, modify the production code
 
93
just enough so the test passes.  If you want the system to do something that
 
94
can't be expressed in one test, write multiple tests, each one interspersed
 
95
with some production development to satisfy *just* *that* new test.  This
 
96
is, in my experience, the best way to ensure that you have good test
 
97
coverage, whilst minimising the production of tests which add no value to
 
98
the system.
 
99
 
 
100
How do I retro-fit unit testing onto an existing codebase?
 
101
------------------------------------------------------------
 
102
 
 
103
IRM already has a significant amount of code written, which would take
 
104
hundreds of tests to cover completely.  A lot of the code is structured in
 
105
such a way as to make it hard to test, as well.  There is little point in
 
106
going back and writing tests for all of this functionality.  It appears to
 
107
work well enough, so writing a complete test suite is really a waste of
 
108
resources.
 
109
 
 
110
However, from now on, every time you want to make some modification (whether
 
111
it be a refactoring, a bug fix, or a feature addition), write one or more
 
112
test cases which demonstrate your desired result:
 
113
 
 
114
Refactoring: Write tests surrounding the functionality you intend to
 
115
        refactor.  Show the test cases accurately represent the desired
 
116
        functionality of the system by ensuring they all run properly.  Then
 
117
        perform the refactoring, ensuring you haven't broken anything by
 
118
        making sure the tests all still run properly.
 
119
 
 
120
Bug fix: Write one or more tests which shows the bug in action -- in other
 
121
        words, it hits the bug, and produces erroneous results.  Then modify
 
122
        the system so that the test passes.  You can be confident that
 
123
        you've fixed the bug, because you have concrete feedback from the
 
124
        tests that the bug no longer exists.
 
125
 
 
126
Feature addition: Put together some tests which invoke the feature you want
 
127
        to add, and test that the feature is working as it should. 
 
128
        Naturally, these tests will fail at first, because the feature
 
129
        doesn't exist.  But you then modify the production code to make the
 
130
        feature work, and you stop when your tests all pass.
 
131
 
 
132
Over time, as old code gets modified, the percentage of code covered by the
 
133
tests will increase, and eventually we will have a comprehensively tested
 
134
piece of software.
 
135
 
 
136
During modifications, if you manage to break something accidentally, write a
 
137
test to show the breakage and fix it from there.  If you broke it once,
 
138
there's a good chance it'll break again when someone else modifies it, and
 
139
there should be a test to immediately warn the programmer that they've
 
140
broken something.
 
141
 
 
142
How do I run the unit tests?
 
143
------------------------------
 
144
 
 
145
The primary script that executes all of the unit tests is the 'alltests'
 
146
script in the testing/ directory of the distribution.  To make it easily
 
147
runnable on all platforms, there is a run script ('run' for Unix, 'run.bat'
 
148
for Windows) which should run all of the tests.
 
149
 
 
150
To use the web test version copy the ini files from testing/data to config/.
 
151
and edit the username and password to match your sql installation, you will
 
152
also need to match the password in ConfigTest.php, and point your browser 
 
153
at http://yourhost/path-to-irm/testing/alltests.php