DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on • Updated on

Electron Adventures: Episode 24: Unit Testing Electron

In previous episode, fileInfo function in preload.js got quite long, so let's write some tests.

We can start by installing jest, which looks like the most popular backend testing framework for Javascript:

$ npm install --save-dev jest
Enter fullscreen mode Exit fullscreen mode

And we can also put it in package.json for convenience:

    "test": "jest"
Enter fullscreen mode Exit fullscreen mode

Problem with testing preload.js

There are two conventions for organizing tests in Javascript - either moving all specs to separate folder, or putting __tests__ folders all over the place. I never really understood the appeal of scattering tests files all over, so we'll be doing the tidy thing and have them all in spec.

The first thing I tried was just creating spec/preload.spec.js and importing preload.js from there. Unfortunately that runs into contextBridge.exposeInMainWorld not being defined.

Rewrite preload.js

So let's do the easy thing - reduce preload.js to just this:

let { contextBridge } = require("electron")
let api = require("./preload/api")

contextBridge.exposeInMainWorld("api", api)
Enter fullscreen mode Exit fullscreen mode

Which doesn't need any unit tests, as it's just one line of code. It will get covered by integration tests later.

And let's move the rest of the code from preload.js to preload/api.js, with just this export:

module.exports = {
  directoryContents,
  currentDirectory,
}
Enter fullscreen mode Exit fullscreen mode

Add tests

I'm not interested in checking low level system calls, so I just created:

  • spec/preload/examples
  • spec/preload/examples/example_01.md - small file with 5 bytes
  • spec/preload/examples/example_02.md - symlink to example_01.md
  • spec/preload/examples/example_03 - directory
  • spec/preload/examples/example_03/.gitkeep - empty file so it actually stays in git, as git does not do empty directories
  • spec/preload/examples/example_04 - link to example_03
  • spec/preload/examples/missing - broken symlink

This is good enough for our test - it won't verify special files, and files deleted while we're running our function, but let's not get too fancy.

Unfortunately we have a few more problems. We don't know what will be the exact Date returned, and if we just put the files in the repository, and check them out, there's no guarantee they'll come out with the same last modified date.

So I thought I'd just do something along the lines of:

expect(result[0].mtime).toBeInstanceOf(Date)
Enter fullscreen mode Exit fullscreen mode

But that returned mysterious:

    Expected constructor: Date
    Received constructor: Date
Enter fullscreen mode Exit fullscreen mode

Looks like node has multiple Date classes? It's very incidental to what we're doing so I didn't investigate it further. If anyone knows the answer, let me know in comments.

spec/preload/api.spec.js

And so here's our spec file:

let path = require('path')
let api = require("../../preload/api.js")

test("currentDirectory", () => {
  expect(api.currentDirectory()).toEqual(path.join(__dirname, "../.."))
})

test("directoryContents", async () => {
  let examplesDir = `${__dirname}/examples`
  let result = await api.directoryContents(examplesDir)

  expect(result).toMatchObject([
    {
      linkTarget: null,
      name: "example_01.md",
      mtime: expect.anything(),
      size: 6,
      type: "file",
    },
    {
      linkTarget: "example_01.md",
      name: "example_02.md",
      mtime: expect.anything(),
      size: 6,
      type: "file",
    },
    {
      linkTarget: null,
      name: "example_03",
      mtime: expect.anything(),
      type: "directory",
    },
    {
      linkTarget: "example_03",
      name: "example_04",
      mtime: expect.anything(),
      type: "directory",
    },
    {
      linkTarget: "example_05.md",
      name: "missing",
      type: "broken",
    },
  ])
})
Enter fullscreen mode Exit fullscreen mode

Of course we could do a lot more testing, but it's a start.

How to test backend code

To be honest we shouldn't even be putting that much code in the preload - it should just do minimum work to setup some calls to the backend, and the logic should be in the backend.

We can do very similar testing for anything in the backend - just move all complicated logic to separate files, and test those files. They are executed in regular node environment.

Then add some integration tests for the whole thing.

Results

Here's the results:

Episode 24 Screenshot

In the next few episodes, we'll be doing some purely frontend coding for our app.

As usual, all the code for the episode is here.

Discussion (0)