UNPKG

15.9 kBMarkdownView Raw
1# util.fixture
2
3> Test fixture library
4
5[![Build Status](https://travis-ci.org/jmquigley/util.fixture.svg?branch=master)](https://travis-ci.org/jmquigley/util.fixture)
6[![tslint code style](https://img.shields.io/badge/code_style-TSlint-5ed9c7.svg)](https://palantir.github.io/tslint/)
7[![Test Runner](https://img.shields.io/badge/testing-jest-blue.svg)](https://facebook.github.io/jest/)
8[![NPM](https://img.shields.io/npm/v/util.fixture.svg)](https://www.npmjs.com/package/util.fixture)
9[![Coverage Status](https://coveralls.io/repos/github/jmquigley/util.fixture/badge.svg?branch=master)](https://coveralls.io/github/jmquigley/util.fixture?branch=master)
10
11A testing fixture class used to simplify managing testing artifacts. A testing fixture in this context is set of files and directories that are copied/loaded into a temporary location, named template values within the files are replaced, the fixture is used for that test, and then it is destroyed when all tests are complete. The constructor looks for fixtures from the root of the project in `./test/fixtures`. This directory contains a set of additional directories. Each subdirectory represents a named fixture that can be used in a test. e.g.
12
13 ./__tests__/fixtures/some-test
14
15This would contain a usable fixture named `some-test`. The name of the fixture is a parameter to the constructor. Within these named fixture directories one can place files and directories used by a test. See the usage section below on how to use this within a test.
16
17The reason for this module is to deal with concurrency in the [ava] test runner. It runs tests concurrently, so using one directory for test fixtures is a problem as the tests will share artifacts incorrectly (think of two tests trying to access the same file and writing different things at the same time). This overcomes that issue by making a separate temporary location each time a fixture is instantiated; different tests will each have their own copy of the fixture.
18
19#### Features
20- Template replacement
21- Automatic parsing of a JSON file within the fixture
22- Parsing of a data file list
23- Can be used with concurrent test processing
24- Execution of a fixture.js script during instantiation
25- Lorem Ipsum and Pattern generators
26
27## Installation
28
29This module uses [yarn](https://yarnpkg.com/en/) to manage dependencies and run scripts for development.
30
31To install as an application dependency with cli:
32```
33$ yarn add --dev util.fixture
34```
35
36To build the app and run all tests:
37```
38$ yarn run all
39```
40
41## Usage
42
43#### Simple Fixture
44```javascript
45const Fixture = require('util.fixture');
46
47let fixture = new Fixture('test-fixture-1');
48let s = fixture.read('somefile.txt');
49
50... // your test
51```
52
53This will copy the contents of the named fixture `test-fixture-1` to a temporary location. The temporary directory location is based on the environment variables `TMP` or `TEMP`. If neither of these are set, then the directory `~/.tmp` is chosen (and created if it doesn't exist). This is the home directory of the user running the test. Within this directory another directory is created named `unit-test-data`. All test fixtures are copied to this location when used. The name of each fixture is a generated UUID to make them unique for each test each time it is exectued. When a new fixture is created it returns an object with attributes related to that fixture (the *attributes* are listed below). The structure of the fixture could be:
54
55```
56./__tests__/fixtures/test-fixture-1/
57 somefile.txt
58 somedirectory/
59 ...
60```
61
62In this example the temporary location `fixture.dir` represents the temporary directory where the fixture was copied and expanded. From this location one would see the directory/file structure above. The creation of the fixture also results in template replacement. In this example there are no custom templates variables; only builtins (template replacement will be explained below).
63
64This example also shows that a file named `somefile.txt` was read from the fixture into a temporary string variable `s` using the `read()`.
65
66
67#### Simple JSON
68```javascript
69const Fixture = require('util.fixture');
70
71let fixture = new Fixture('simple-json');
72
73... // your test
74```
75
76Similar to a simple fixture above. If the fixture contains a file with the name `obj.json` then it will load the fixture, parse this JSON, perform template replacement, and save it within the fixture in an exposed field named `fixture.obj`. The structure of the fixture would be:
77
78```
79./__tests__/fixtures/simple-json/
80 obj.json
81 somedirectory/
82 ...
83```
84
85
86#### JSON with Template Replacement
87```javascript
88const Fixture = require('util.fixture');
89
90let fixture = new Fixture('some-fixture', {
91 templateData: {
92 replaceMe: 'test data'
93 }
94});
95
96... // your test
97```
98
99Loads a JSON file saved in a fixture location and replaces custom text strings using template replacement. This would search the given JSON file for all instances of the string `{replaceMe}` and substitute the given value in the template (`test data`). An example JSON in this case would be:
100
101```json
102{
103 "testData": "{replaceMe}",
104 "testBool": true
105}
106```
107resulting in:
108```json
109{
110 "testData": "test data",
111 "testBool": true
112}
113```
114
115It will lead to a fixture object returned like the previous two examples.
116
117
118#### Fixture with Template Replacement
119```javascript
120const Fixture = require('util.fixture');
121
122let fixture = Fixture('test-fixture', {
123 jsonFile: 'test-directory/somefile.json',
124 dataFile: 'test-file.txt',
125 templateData: {
126 replaceMe: 'test data'
127 }
128});
129
130... // your test
131```
132
133Loads a fixture and then searches through all of the files in that fixture for template replacement values. The same replacements are applied to all files. This example also demonstrates the use of optional parameters to change the names of the JSON file and data file name. An example directory structure for `test-fixture` within the temporary location would be:
134
135```
136./__tests__/fixtures/test-fixture/
137 test-directory/
138 somefile.json
139 test-file.txt
140```
141
142The constructor would copy the fixture to the temporary location, perform template replacement on each of the files (ignoring directories), save them to their temporary versions, and then parse the `jsonFile` and `dataFile` parameters into `fixture.obj` and `fixture.data`. The example text file above named `test-file.txt` would be:
143
144```
145Test information
146
147{replaceMe}
148```
149
150And after replacement would be:
151
152```
153Test information
154
155test data
156```
157
158This sample file would also be [parsed as a file list](https://www.npmjs.com/package/util.filelist). This would read each line from the file (ignoring blank lines and # comments) and save each line into an array named `fixture.data`. This is a way to take a large list of data, parse it, and store it into an array.
159
160
161#### Empty Fixture (Temporary Directory)
162```javascript
163const Fixture = require('util.fixture');
164
165let fixture = Fixture('tmpdir');
166
167... // your test
168```
169
170A fixture with the name `tmpdir` is a special case. This will create a temporary directory, but will have no files or directories within it. It will not perform any template replacements. The directory is accessible through `fixture.dir`.
171
172A temporary directory can also be created with an empty constructor:
173
174```javascript
175const Fixture = require('util.fixture');
176
177let fixture = Fixture();
178
179... // your test
180```
181
182When the fixture is cleaned up this directory would be removed.
183
184
185#### Pattern Generator
186When the fixture is instatiated a test pattern string is created. This string represents a 2D array of repeated chevrons. Note that this data is stored as a newline delimited string.
187
188The default pattern is a set of chevrons of the letters `a` - `z`, where each letter is a row of 80 columns. e.g.
189
190```
191aaaaaaaaaa...
192bbbbbbbbbb...
193cccccccccc...
194...
195zzzzzzzzzz...
196```
197
198To create a default pattern use:
199
200```javascript
201const fixture = new Fixture('pattern');
202fixture.pattern; // a string representing the pattern.
203```
204
205To create a custom pattern use:
206
207```javascript
208const fixture = new Fixture('pattern', {
209 pattern: {
210 chevrons: ['0', '1', '2'],
211 columns: 80,
212 repeat: 5
213 }
214});
215```
216This will create a 15 x 80 grid of numbers, where each row is `0, 1, 2, ...` (15 rows).
217
218
219#### LoremIpsum Generator
220When the `Fixture` is instantiated it will generate a [lorem ipsum](https://en.wikipedia.org/wiki/Lorem_ipsum) string (random text). It uses the [lorem-ipsum](https://github.com/knicklabs/lorem-ipsum.js) package to create the text. The text is accessed through the property `.loremIpsum`. See the package link above for configuration options. A special name `loremIpsum` can be used if no fixture directory structure is required.
221
222```javascript
223const fixture = new Fixture('loremIpsum', {
224 loremIpsum: {
225 count: 3,
226 sentenceLowerBound: 10,
227 sentenceUpperBound: 20
228 }
229});
230
231fixture.loremIpsum;
232```
233
234The fixture above generates a custom lorem ipsum string with 3 sentences where each sentence has between 10 and 20 words.
235
236
237#### Script Execution
238If the fixture contains a script named `fixture.js` then this script will be executed when the fixture is instantiated. Note that this script will only work with built in node *requires* unless the fixture itself contains its own `node_modules` directory. The script is removed from the temporary fixture after it is executed.
239
240The name of the execution script can be changed as an optional parameter to the fixture construtor:
241
242```javascript
243const Fixture = require('util.fixture');
244
245let fixture = Fixture('test-fixture', {
246 script: 'ascript.js'
247});
248
249... // your test
250```
251
252
253#### Cleanup
254When all tests are complete the fixture should be cleaned up. The class contains a static method named `cleanup`. In [ava] this is used in the `test.after.always` hook:
255
256```javascript
257test.after.always.cb(t => {
258 Fixture.cleanup((err: Error, directories: string[]) => {
259 if (err) {
260 t.fail(err.message);
261 }
262
263 directories.forEach((directory: string) => {
264 t.false(fs.existsSync(directory));
265 });
266
267 t.end();
268 });
269});
270```
271
272The cleanup function only needs to be called once per testing file. The class keeps track of all test directories that were created when the Fixture is instantiated and removes them when the cleanup is called.
273
274Note that when using [ava] the hook `test.after.always` is executed within each separate test file and NOT once per overall test execution. This code needs to be part of each test file to clean up the files related to those tests. If this is not executed, then all of the temp files created by the fixture will remain (and you will be required to clean them up). Note that this process has intermittent issues on Windows. It seems that the [fs-extra remove function](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove.md) will occasionally fail when trying to delete files if Windows still has a process attached to it (like file explorer).
275
276
277See [tests.js](https://github.com/jmquigley/util.fixture/blob/master/test/tests.ts) in this repository for examples of these usage patterns.
278
279
280## API
281
282### Fixture([{name}, opts])
283
284This is a single constructor exposed by the module.
285
286##### parameters
287
288- `name {string}`: The name of the fixture to use. This corresponds to an entry in `./__tests__/fixtures/{name}`. When this is empty an empty, temporary directory is created.
289- `opts: {object}`: optional parameters (listed below)
290
291##### options
292
293The following options can be used to customize the fixture. They can be set as an optional object given to the class during instantiation or within `package.json` in a section named *fixture*. The precedence of application, from lowest to highest, is the default internal options, the `package.json`, and finally the constructor options.
294
295- `basedir {string}`: The base location where the fixture will be temporarily located. The default location is determined by the environment variable `TMP` first or `TEMP` if TMP is not found. If neither of these are set, then `~/.tmp/unit-test-data` is created and used within the users home directory. This must be a directory that is writable by the user running the test.
296- `dataFile {string}`: The name of the data list file, within the fixture location, that will be parsed and saved into `fixture.data` as an array of lines. By default this file is `data.list`. It is parsed by the [util.filelist](https://www.npmjs.com/package/util.filelist) module. This is a way to get a large list of information into the fixture.
297- `fixtureDirectory {string}`: The location within the project where fixtures are found. The default is `./__tests__/fixtures`.
298- `jsonFile {string}`: The name of a JSON data file that will be parsed and saved into `fixture.obj`. By default this file is named `obj.json` within the fixture.
299- `pattern {object}` - configuration overrides for the pattern generator. It contains 3 attributes: columns, repeat count, and an array of chevrons.
300- `loremIpsum {object}`: [configuration options](https://github.com/knicklabs/lorem-ipsum.js/blob/master/README.md) for the lorem ipsum generator.
301- `script {string}`: The name of the node script that will be executed when the fixture is instantiated. The default nam is `fixture.js`.
302- `templateData {object}`: a map of key/value pairs that are used for replacement within each fixture file. The [string-template](https://www.npmjs.com/package/string-template) library is used to perform the replacement. All files are checked.
303
304##### attributes
305Instantiation of the class returns an object with the following attributes:
306
307- `.basedir` - the root temporary directory for all tests. This can be changed as an option to the function constructor
308- `.cleanup()` - static method on the class that removes the base directory and and all artifacts copied there. Generally this would be used at the end of ALL testing. In [ava] this would be done in the `test.after.always` function.
309- `.data` - if the fixture contains a file named `data.list` or a text file named by the `dataFile` option, then it will be processed and placed here. This is an Array object when defined.
310- `.dir` - the location of the temporary directory created for this fixture.
311- `.files` - an array of files that were found within the fixture and placed into the temporary `.dir`.
312- `.name` - the name of the requested fixture. This is the first string parameter to the Fixture constructor.
313- `.loremIpsum` - a string containing a randomly generated lorem ipsum string/sentence.
314- `.obj` - if the fixture contains `obj.json` or a JSON file named by the `jsonFile` option, then it is parsed and the contents of that JSON are stored here. The JSON file will go through template replacement before it is parsed.
315- `.pattern` - a string contained a MxN repeating pattern.
316- `.read({filename}): string` - Reads the contents of one of the files within the fixture and returns it as a string. The filename is a relative path within the fixture (the absolute path is resolved by the class). This is the contents of the file after it has been processed through template replacement.
317- `.src` - the absolute directory path for the fixture files.
318- `.toString()` - returns a string that shows the internal representation of the fixture. It will show all of these attributes and the options that were passed to the class when it was instantiated.
319
320##### events
321
322- `loaded` - This event fires once the constructor has finished creating the fixture.
323
324## Template Data Variables
325The following are template variables that automatically added to the variable expansion list (templateData).
326
327- `DIR`: the location where the fixture will be copied. e.g.
328
329```
330{
331 file: "{DIR}/somefile.txt"
332}
333```
334
335[ava]: https://github.com/avajs/ava