|
1 | 1 | # Mocha CodeRoad
|
2 | 2 |
|
3 |
| -[Atom-CodeRoad](https://github.com/coderoad/atom-coderoad) test runner & reporter. |
| 3 | +[Atom-CodeRoad](https://github.com/coderoad/atom-coderoad) Javascript test runner & reporter. |
4 | 4 |
|
5 | 5 | npm install mocha-coderoad
|
| 6 | + |
| 7 | + |
| 8 | +### Test Statements |
| 9 | + |
| 10 | +It makes sense to write test statements using 'should', 'must' or negative statements. Remember, the failing test message will be delivered as feedback to the user. |
| 11 | + |
| 12 | +```js |
| 13 | +it('should be a function') |
| 14 | +it('must be a function') |
| 15 | +it('isn\'t a function') |
| 16 | +``` |
| 17 | + |
| 18 | +### Loaders |
| 19 | + |
| 20 | +Use a **loader** to run the user saved file in the context of your file. Think of a loader as a way to place the file your testing inside of your test file. Import your loader and run it on a specific user file. |
| 21 | + |
| 22 | +```js |
| 23 | +var loadJS = require('path/to/loadJS').default; |
| 24 | +loadJS('user-file.js'); |
| 25 | +// adds file contents here |
| 26 | +``` |
| 27 | + |
| 28 | +You'll have to roll your own loader to fit your project, but there are example [loaders](https://coderoad.github.io/docs/#loaders) included later in the docs. |
| 29 | + |
| 30 | +*Note: When using spies, stubs or mocks, initiate them above your loader call.* |
| 31 | + |
| 32 | +Tutorials may be written in different programming languages or for different compilers, so there isn't yet a standard way to load data from user created files. Instead, you'll have to load/transpile your files for the test runner. Rolling your own solution allows you to load data in a way that fits your project. |
| 33 | + |
| 34 | +There may be a simpler approach in the future, but for now these snippets should help: |
| 35 | + |
| 36 | + |
| 37 | +#### loadJS (JavaScript) |
| 38 | + |
| 39 | +```js |
| 40 | +var vm = require('vm'), |
| 41 | + fs = require('fs'), |
| 42 | + path = require('path'); |
| 43 | +function loadJS(pathToContext) { |
| 44 | + var absPath = path.join(process.env.DIR, pathToContext); |
| 45 | + var code = fs.readFileSync(absPath, 'utf8'); |
| 46 | + vm.runInThisContext(code); |
| 47 | +} |
| 48 | +Object.defineProperty(exports, "__esModule", { value: true }); |
| 49 | +exports.default = loadJS; |
| 50 | +``` |
| 51 | + |
| 52 | +#### loadBabel (Babel) |
| 53 | + |
| 54 | +See the [Babel documentation](https://babeljs.io/docs/setup/#node) on how to install Babel & the selected [presets](http://babeljs.io/docs/plugins/#presets). Set the [options](http://babeljs.io/docs/usage/options/) you need. |
| 55 | + |
| 56 | +`> npm i -s babel-core` + presets |
| 57 | + |
| 58 | +```js |
| 59 | +var vm = require('vm'), |
| 60 | + fs = require('fs'), |
| 61 | + path = require('path'), |
| 62 | + babel = require('babel'), |
| 63 | + options = { |
| 64 | + presets: ['es2015'] |
| 65 | + }; |
| 66 | +function loadBabel(pathToContext) { |
| 67 | + var absPath = path.join(process.env.DIR, pathToContext); |
| 68 | + var code = fs.readFileSync(absPath, 'utf8'); |
| 69 | + var js = babel.transform(code, options); |
| 70 | + vm.runInThisContext(js); |
| 71 | + } |
| 72 | +Object.defineProperty(exports, "__esModule", { value: true }); |
| 73 | +exports.default = loadBabel; |
| 74 | +``` |
| 75 | + |
| 76 | +#### loadTS (TypeScript) |
| 77 | + |
| 78 | +To use TypeScript, install the package as a tutorial dependency. |
| 79 | + |
| 80 | +`> npm i -s typescript` |
| 81 | + |
| 82 | +```js |
| 83 | +var ts = require('typescript'), |
| 84 | + options = { |
| 85 | + module: 'commonjs', |
| 86 | + target: 'ES5' |
| 87 | + }; |
| 88 | +function loadTS(pathToContext) { |
| 89 | + var absPath = path.join(process.env.DIR, pathToContext); |
| 90 | + var code = fs.readFileSync(absPath, 'utf8'); |
| 91 | + var js = ts.transpile(code, options); |
| 92 | + vm.runInThisContext(js); |
| 93 | + } |
| 94 | +Object.defineProperty(exports, "__esModule", { value: true }); |
| 95 | +exports.default = loadTS; |
| 96 | +``` |
| 97 | + |
| 98 | + |
| 99 | +### Loading Data Files |
| 100 | + |
| 101 | +Data can be loaded in the user's file by setting it as a global within the test. Remember, the top of the test file (above the loader), acts as the top of the user's page. |
| 102 | + |
| 103 | +Although bad practice, it can be easiest to set data to the global scope. |
| 104 | + |
| 105 | +```js |
| 106 | +if (!global.data) { |
| 107 | + global.data = 43; |
| 108 | +} |
| 109 | + |
| 110 | +if (!global.data2) { |
| 111 | + global.data2 = JSON.parse(JSON.stringify(require('./data2.json'))); |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +Users can access global data by name in their file. |
| 116 | + |
| 117 | +```js |
| 118 | +var secret = data - 1; |
| 119 | +``` |
| 120 | + |
| 121 | +Here are examples using *mocha* with *chai*'s *expect*. See the [docs](http://chaijs.com/api/bdd/). |
| 122 | + |
| 123 | +#### exists |
| 124 | + |
| 125 | +```js |
| 126 | +it('doesn\'t exist', function() { |
| 127 | + expect(target).to.not.be.undefined; |
| 128 | +}); |
| 129 | +``` |
| 130 | + |
| 131 | +#### type |
| 132 | + |
| 133 | +```js |
| 134 | +it('should be a function', function() { |
| 135 | + expect(target).to.be.a('function'); |
| 136 | +}); |
| 137 | +``` |
| 138 | + |
| 139 | +#### function params |
| 140 | + |
| 141 | +```js |
| 142 | +it('should have two parameters', function() { |
| 143 | + expect(target).to.have.length(2); |
| 144 | +}); |
| 145 | +``` |
| 146 | + |
| 147 | +#### function returns |
| 148 | + |
| 149 | +```js |
| 150 | +it('should add one to the number', function () { |
| 151 | + expect(addOne(1)).to.equal(2); |
| 152 | +}); |
| 153 | +``` |
| 154 | + |
| 155 | +#### equals |
| 156 | + |
| 157 | +```js |
| 158 | +it('should be 42', function () { |
| 159 | + expect(target).to.equal(42); |
| 160 | +}); |
| 161 | +``` |
| 162 | + |
| 163 | +#### deep equals (with objects or arrays) |
| 164 | + |
| 165 | +```js |
| 166 | +it('should be {a: 42}', function () { |
| 167 | + expect(target).to.deep.equal({a: 42}); |
| 168 | +}); |
| 169 | +``` |
| 170 | + |
| 171 | +#### regex |
| 172 | + |
| 173 | +```js |
| 174 | +it('should include the variable "count"', function () { |
| 175 | + var regex = new RegExp('count'); |
| 176 | + var string = target.toString(); |
| 177 | + expect(string.match(regex)).to.be.true; |
| 178 | +}); |
| 179 | +``` |
| 180 | + |
| 181 | +```js |
| 182 | +it('should access the property "prop"', function () { |
| 183 | + var regex1 = /\.prop/; // dot notation |
| 184 | + var regex2 = /\[["']prop["']\]/; // bracket notation |
| 185 | + var string = target.toString(); |
| 186 | + var result = !!string.match(regex1) || !!string.match(regex2); |
| 187 | + expect(result).to.be.true; |
| 188 | +}); |
| 189 | +``` |
| 190 | + |
| 191 | +#### spies |
| 192 | + |
| 193 | +You can use [*sinon*](http://sinonjs.org/docs/) or [*chai-spies*](https://github.com/chaijs/chai-spies) to create a spy. See an example below: |
| 194 | + |
| 195 | +`> npm i -s chai-spies` |
| 196 | + |
| 197 | +```js |
| 198 | +var chai = require('chai'), |
| 199 | + spies = require('chai-spies'); |
| 200 | +var expect = chai.expect; |
| 201 | + chai.use(spies); |
| 202 | + |
| 203 | +var spy = chai.spy.on(console, 'log'); |
| 204 | +loadJS('04-forEach.js'); |
| 205 | + |
| 206 | +it('should write "hello world" to the console', function () { |
| 207 | + expect(spy).to.have.been.called.with('hello world'); |
| 208 | +}); |
| 209 | +``` |
| 210 | + |
| 211 | +### Dynamic Tests |
| 212 | + |
| 213 | +There are situations where you might want to change data based on the current task. In this case, be careful, as all earlier tests must continue to pass. |
| 214 | + |
| 215 | +Some variables are passed into the test runner through the node environment *process.env*. |
| 216 | + |
| 217 | +See an example of dynamic data based on the task position below: |
| 218 | + |
| 219 | +```js |
| 220 | +var data = [1, 2, 3]; |
| 221 | + |
| 222 | +if (process.env.TASK_POSITION === '4') { |
| 223 | + data = [1, 2, 3, 4]; |
| 224 | +} |
| 225 | +``` |
| 226 | + |
| 227 | +Tests can also change based on the task position. |
| 228 | + |
| 229 | +```js |
| 230 | +if (process.env.TASK_POSITION !== '4') { |
| 231 | + it('should do this', function () { ... }); |
| 232 | +} else { |
| 233 | + it('should to that', function () { ... }); |
| 234 | +} |
| 235 | +``` |
| 236 | + |
| 237 | +See a full [example](https://github.com/coderoad/coderoad-functional-school/blob/master/tutorial/1/04/01-forEach.spec.js). |
| 238 | + |
| 239 | +### Test Writing Tool |
| 240 | + |
| 241 | +It's entirely possible to create a simplified testing tool that could make writing tests faster & easier. |
| 242 | + |
| 243 | +The easiest solution would be to use editor snippets to generate a page of tests from a simple configuration object. This does not yet exist. |
| 244 | + |
| 245 | +## Config |
| 246 | + |
| 247 | +CodeRoad tutorial configurations can be set in the *package.json* config. |
| 248 | + |
| 249 | +```json |
| 250 | +{ |
| 251 | + "config": { |
| 252 | + "testDir": "tutorial", |
| 253 | + "testSuffix": ".spec.js", |
| 254 | + "testRunner": "mocha-coderoad", |
| 255 | + "edit": true |
| 256 | + } |
| 257 | +} |
| 258 | +``` |
| 259 | + |
| 260 | +This section will likely expand in the future. For now, let's go over some of the configurations. |
| 261 | + |
| 262 | +#### testDir |
| 263 | + |
| 264 | +The relative path to the unit test directory. This makes writing unit tests paths easier. |
| 265 | + |
| 266 | +#### testSuffix |
| 267 | + |
| 268 | +The common suffix for unit tests. Also making writing unit test paths shorter. |
| 269 | + |
| 270 | +#### testRunner |
| 271 | + |
| 272 | +Specified test runner. Currently only "mocha-coderoad" is available. |
| 273 | + |
| 274 | +#### edit |
| 275 | + |
| 276 | +If set to true, *Atom-CodeRoad* will allow users to submit issues or submit markdown pull requests. You will also need to specify "repository" & "bugs" in your *package.json* file. See an [example config file](https://github.com/coderoad/coderoad-functional-school/blob/master/package.json). |
0 commit comments