Skip to content

Commit 27b5d25

Browse files
committed
split out testing from readme
1 parent 79d5b60 commit 27b5d25

File tree

2 files changed

+250
-248
lines changed

2 files changed

+250
-248
lines changed

developer-doc/testing.md

+248
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# Testing: `test/`
2+
3+
Lab's JavaScript tests use [Vows](http://vowsjs.org), an asynchronous behavior driven framework based
4+
on [Node.js](http://nodejs.org/). In addition Lab uses [jsdom](https://github.com/tmpvar/jsdom), a
5+
lightweight CommonJS implementation of the W3C DOM specifications. Lab's test setup was inspired
6+
by that used by [d3.js](http://mbostock.github.com/d3/). The development dependencies for running the
7+
tests are installed using [npm](http://npmjs.org/).
8+
9+
Running the tests:
10+
11+
$ make test
12+
................................. . . .. . . .
13+
x OK > 40 honored (0.012s)
14+
15+
If you are running `bin/guard` the tests run automatically anytime a change is made in the JavaScript
16+
files in the `src/` or `test/` directory.
17+
18+
The results of the tests are displayed in the console that `bin/guard` is running in.
19+
20+
If the bottom of the console window is viewable you will see new test results whenever you save a changes.
21+
22+
Recent versions of nodejs/v8 support TypedArrays -- this make it possible to more extensively
23+
test lab.arrays which is designed to support using either typed or regular arrays for computation.
24+
25+
`test/env.js` uses the node module [jsdom](https://github.com/tmpvar/jsdom) to setup resources for
26+
simple emulation of a browser.
27+
28+
[Vows](http://vowsjs.org) integrates the [standard nodejs assertions](http://nodejs.org/docs/latest/api/assert.html)
29+
with an additional collection of useful [assertions](http://vowsjs.org/#assertions) summarized below:
30+
31+
- numerical
32+
33+
assert.greater (3, 2);
34+
assert.lesser (2, 3);
35+
assert.inDelta (Math.random(), 0, 1);
36+
37+
- equality
38+
39+
assert.equal (4, 4);
40+
assert.strictEqual (4 > 2, true);
41+
assert.notEqual (4, 2);
42+
assert.strictNotEqual (1, true);
43+
assert.deepEqual ([4, 2], [4, 2]);
44+
assert.notDeepEqual ([4, 2], [2, 4]);
45+
46+
- type
47+
48+
assert.isFunction (function () {});
49+
assert.isObject ({goo:true});
50+
assert.isString ('goo');
51+
assert.isArray ([4, 2]);
52+
assert.isNumber (42);
53+
assert.isBoolean (true);
54+
assert.typeOf (42, 'number');
55+
assert.instanceOf ([], Array);
56+
57+
- truth
58+
59+
assert.isTrue (true);
60+
assert.isFalse (false);
61+
62+
- null, undefined, NaN
63+
64+
assert.isNull (null);
65+
assert.isNotNull (undefined);
66+
assert.isUndefined ('goo'[9]);
67+
assert.isNaN (0/0);
68+
69+
- inclusion
70+
71+
assert.include ([4, 2, 0], 2);
72+
assert.include ({goo:true}, 'goo');
73+
assert.include ('goo', 'o');
74+
75+
- regexp matching
76+
77+
assert.match ('hello', /^[a-z]+/);
78+
79+
- length
80+
81+
assert.length ([4, 2, 0], 3);
82+
assert.length ('goo', 3); *** not working ***
83+
84+
- emptiness
85+
86+
assert.isEmpty ([]);
87+
assert.isEmpty ({});
88+
assert.isEmpty ("");
89+
90+
- exceptions
91+
92+
assert.throws(function () { x + x }, ReferenceError);
93+
assert.doesNotThrow(function () { 1 + 1 }, Error);
94+
95+
Additionally `test/env-assert.js` has a number of useful additional assertions copied from
96+
[d3.js](http://mbostock.github.com/d3/).
97+
98+
_**Note**: Using a more specific assertion usually results in more useful error reports._
99+
100+
There are also many interesting test examples and patterns in the
101+
[d3.js test directory](https://github.com/mbostock/d3/tree/master/test) that can be adapted for use in Lab.
102+
103+
## A Simple Example of Test Driven Development
104+
105+
Here's a simple example that is part of the tests for `lab.arrays.js` to test the `arrays.max()` function:
106+
107+
"find max in array with negative and positive numbers": function(max) {
108+
assert.equal(max([3, -1, 0, 1, 2, 3]), 3);
109+
},
110+
111+
The 'model stepping' tests are a good example where the tests help helped drive new features. The basic
112+
features I was testing in this section relate to the existing functionality exposed by the Stop, Start, Go, and
113+
Reset buttons as wells as the extended keyboard controls that allow stepping forward and backwards a step at a time.
114+
115+
First I created this test that passed:
116+
117+
"after running running one tick the model is at step 1": function(model) {
118+
model.tick();
119+
assert.equal(model.stepCounter(), 1);
120+
assert.isTrue(model.isNewStep());
121+
},
122+
123+
In thinking about driving out changes to KE, PE and Temperature of the molecular model itself I realized
124+
I'd like the capability to run a specific number of steps forward and then check the results.
125+
126+
I then wrote this test that failed -- because the model.tick() function didn't yet take an optional argument to
127+
run multiple steps forward:
128+
129+
"after running 9 more ticks the model is at step 10": function(model) {
130+
model.tick(9);
131+
assert.equal(model.stepCounter(), 10);
132+
assert.isTrue(model.isNewStep());
133+
},
134+
135+
After saving the change I saw the new test failure reported in my console. I then implemented the new
136+
feature in the actual `src/lab/molecules.js`. Less than a second after saving the file the tests
137+
completed and the report showed it passing.
138+
139+
This is a very simple example -- but part of the value of this kind of test driven development is in first
140+
thinking of how something should behave rather than in how to get it to actually do the work.
141+
142+
Since I already had this function for running one model step:
143+
144+
model.tick()
145+
146+
Adding an optional numeric argument for running more steps is a fine way to express the intent of the new feature:
147+
148+
model.tick(9)
149+
150+
In more complicated coding thinking about how to express the intent clearly and then what the result
151+
should be if that intent is successful **FIRST** ... and then 'driving out' the actual implementation to
152+
achieve that result can result in a better architecture -- and of course you also end up with tests.
153+
154+
Because the tests run SO quickly I can interactively change the code in the module or the test and
155+
immediately see results.
156+
157+
## Debugging Tests using the node debugger
158+
159+
Sometimes it can be helpful to break into a debugger when there is a problem in either the code
160+
or the test setup itself. Node comes with a [debugger](http://nodejs.org/docs/latest/api/debugger.html)
161+
which can be used in combination with vows and the tests.
162+
163+
First set a breakpoint by inserting the statement: `debugger;`
164+
165+
suite.addBatch({
166+
"Thermometer": {
167+
topic: function() {
168+
debugger;
169+
return new components.Thermometer("#thermometer");
170+
},
171+
"creates thermometer": function(t) {
172+
assert.equal(t.max, 0.7)
173+
}
174+
}
175+
});
176+
177+
Start the node debugger and pass in the full command line to run the tests:
178+
179+
node debug ./node_modules/vows/bin/vows --no-color
180+
181+
The debugger will break at the beginning of vows:
182+
183+
< debugger listening on port 5858
184+
connecting... ok
185+
break in node_modules/vows/bin/vows:3
186+
1
187+
2
188+
3 var path = require('path'),
189+
4 fs = require('fs'),
190+
5 util = require('util'),
191+
192+
Enter `cont` to continue execution until your breakpoint.
193+
194+
debug> cont
195+
< ·
196+
< ········
197+
< ·
198+
<
199+
break in test/lab/components/components-test.js:13
200+
11 "Thermometer": {
201+
12 topic: function() {
202+
13 debugger;
203+
14 return new components.Thermometer("#thermometer");
204+
15 },
205+
206+
To evaluate expressions type `repl` -- use ctrl-C to exit the repl:
207+
208+
repl
209+
Press Ctrl + C to leave debug repl
210+
> initialization_options
211+
{ model_listener: false,
212+
lennard_jones_forces: true,
213+
coulomb_forces: true }
214+
> atoms[0].charge
215+
-1
216+
217+
Enter **ctrl-C** to exit the repl and return to the debugger.
218+
219+
Enter **ctrl-D** to exit the debugger.
220+
221+
[node-inspector](https://github.com/dannycoates/node-inspector)
222+
[npm package for node-inspector](http://search.npmjs.org/#/node-inspector)
223+
224+
### Using node-inspector while debugging
225+
226+
[node-inspector](https://npmjs.org/package/node-inspector) supports using the webkit inspector in Chrome to support
227+
interactive debugging in node. This can be particualrly help when debugging tests.
228+
229+
Example:
230+
231+
Add a debugger statement to your test:
232+
233+
originalModelJson = fs.readFileSync(testDir + "expected-json/" + modelJsonFile).toString();
234+
modelName = /\/?([^\/]*)\.json/.exec(modelJsonFile)[1];
235+
debugger;
236+
originalModel = JSON.parse(originalModelJson);
237+
model = new Model(originalModel);
238+
239+
Start a debugging session:
240+
241+
$ node --debug-brk ./node_modules/vows/bin/vows --no-color test/vows/mml-conversions/deserialize-serialize-test.js
242+
debugger listening on port 5858
243+
244+
Start node-inspector:
245+
246+
$ ./node_modules/.bin/node-inspector &
247+
info - socket.io started
248+
visit http://0.0.0.0:8080/debug?port=5858 to start debugging

0 commit comments

Comments
 (0)