Game of Life in JavaScript - Part 2

Roll up, roll up. You waited a whole 2 days for this and here it is - the second part of my “n” part series on writing a Game of Life implementation using JavaScript and Jest, the testing framework.

If you’ve come to this article directly, don’t forget to read part 1 here so that you know where I began with this series.

On today’s menu is getting Jest installed and writing a couple of basic tests so that you can proudly tell your friends and family that you know TDD.

Installation

To be fair, the online getting started section is really good, but that won’t write a blog post, so here’s my take on installing this fantastic framework.

To begin, we’ll use NPM to install the software.

1
2
3
C:\>mkdir game-of-life
C:\>cd game-of-life
C:\game-of-life>npm init

NPM will ask you a whole bunch of questions which you can fill in if you like, but for simplicit, I would just press enter for each question. At the end, type: “yes”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
C:\game-of-life>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (game-of-life)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to C:\game-of-life\package.json:

{
"name": "game-of-life",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}


Is this OK? (yes) yes

If you don’t have npm installed (you will soon realise), you can go and grab it here. If so, do that now and try the command above again.

Looking through the output, near the end of the file where you see the JSON (the stuff inside curly braces), we have the package.json contents.

Let’s now add to that by installing Jest, the test framework.

1
C:\game-of-life>npm install --save-dev jest

The --save-dev flag means that we only want to use this for development purposes - i.e. don’t install into production. Realistically, we won’t be creating the next Amazon with this package, but it does keep things separate at least. Lastly, jest in the command line above is the package name, as you might have guessed.

After installation, you will see something similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C:\game-of-life>npm install --save-dev jest
npm WARN deprecated request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142

added 536 packages, and audited 537 packages in 13s

24 packages are looking for funding
run `npm fund` for details

16 moderate severity vulnerabilities

To address all issues, run:
npm audit fix

Run `npm audit` for details.

The deprecations aren’t a huge problem and the vulnerabilities might be were we to make this public, but we’re fine for what we are doing here. It’s a small, personal, project that will live on our hard-drives.

At this point, we will now have a folder and file in our directory. On my Windows system, here’s how it looks, but if you are using a Mac or Linux, it will differ a little.

1
2
3
4
5
6
7
8
9
10
11
12
13
C:\game-of-life>dir
Volume in drive C has no label.
Volume Serial Number is 86B1-67CE

Directory of C:\game-of-life

06/05/2021 21:09 <DIR> .
06/05/2021 21:09 <DIR> ..
06/05/2021 21:09 <DIR> node_modules
06/05/2021 21:09 407,761 package-lock.json
06/05/2021 21:09 53 package.json
2 File(s) 407,814 bytes
3 Dir(s) 2,118,385,664 bytes free

So far, so good. A quick peek inside the package.json tells us what that command just did so let’s take a look now.

1
2
3
4
5
6
C:\game-of-life>type package.json
{
"devDependencies": {
"jest": "^26.6.3"
}
}

We can see that our installation of Jest is in the development section and that it is already up to version 26.6.3. When you do this, you will probably see a different number, and that’s OK. Things change rapidly in the JavaScript world.

We’ve got the software installed in our folder (in node_modules), but we can’t run it yet, at least not based on how we’ve installed it. The next step is to add a command into the package.json file tell it that when we want to run a test, it should use jest.

Whilst package.json is open in your text editor, find this line:

1
"test": "echo \"Error: no test specified\" && exit 1"

and change it to:

1
"test": "jest"

Save that.

Running the Test Harness

We can test that is now working by typing the following onto the command line:

1
2
3
4
5
6
7
8
9
10
11
12
13
C:\game-of-life>npm test

> test
> jest

No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In K:\github\game-of-life
2 files checked.
testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
testPathIgnorePatterns: \\node_modules\\ - 2 matches
testRegex: - 0 matches
Pattern: - 0 matches

Your first thought might be that it has all gone wrong but no, actually everything is fine. Take a look at the line which reads “No tests found, exiting with code 1”; we just don’t have any tests for it to find and run, so we’re good. Let’s do that next.

So what test files is it actually looking for? The regular expression above (all those astericks) is looking for any files named test.js or something.test.js, where something is a valid filename segment. Here’s some examples:

1
2
3
test.js
my.test.js
this.is.a.big.file.test.js

Adding a Simple Test

For now, create a file called basic.test.js and add the following into it:

1
2
3
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});

This is straight out of the guide and is an example of a matcher. That’s what Jest names the way in which you are testing a value to be true or otherwise. In this case, the toBe() function is a bit like an equality operator in that it is testing that 2 + 2 will be 4.

Reading the function from top to bottom, we could interpret it this way:

I want to create a test with the label: “two plus two is four”. This test will expect that when you add 2 plus 2, the answer will be 4.

Run your test harness - the name we give to the runner or system that will execute your tests - with the following command:

1
>npm test

All being well, you should see something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\game-of-life> npm test

> test
> jest

PASS ./this.is.a.big.file.test.js
√ two plus two is four (1 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.647 s
Ran all test suites.

Yours will probably be nicely colour-coded, but either way, what a lovely thing to see your tests pass (legitimately!).

Seems quite straight-forward right? Let’s try a different matcher - something to do with strings instead of numbers.

Create another test file, this time named: string.test.js and place the following test code inside:

1
2
3
4
5
6
7
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});

test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});

Here, again from the docs, we’re using regular expressions to search for strings inside other strings. What makes this first test a little different is the use of the not function which inverts the nature of the matcher. Stick it in front of a toBe() and it’s the same as saying it is not toBe().

Before you run these tests, what do you think will happen? This is the output from my machine:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\game-of-life> npm test

> test
> jest

PASS ./basic.test.js
PASS ./string.test.js

Test Suites: 2 passed, 2 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.545 s
Ran all test suites.

I’ve deliberately left the other test file in there (basic.test.js) so that you can see what happens if Jest can find others. Here, it tells us that both test files passed and that 3 tests in total ran: the 1 from basic.test.js and 2 others from string.test.js.

There are a whole bunch more matchers which you can read about here but we’ll cover those as we need them when we return to the very exquisite Game of Life, in part 3. One more thing you might want to take a look at is this great intro video on Jest by Karl Hadwen; it covers a bunch of stuff from very simple testing to some quite advanced mocking etc.


Hi! Did you find this useful or interesting? I have an email list coming soon, but in the meantime, if you ready anything you fancy chatting about, I would love to hear from you. You can contact me here or at stephen ‘at’ logicalmoon.com