Switching from Mocha to Tape and testing E2E

Switching from Mocha to Tape & testing E2E

Before reading this post, it might make sense to check out my previous posts on the topic:

  1. Part 1 - Unit testing with JSPM, Mocha, and Vue.js
  2. Part 2 - More testing with JSPM, Mocha, and Vue.js
  3. Part 3 - Testing the DOM
  4. Part 4 - Switching from mocha to tape

BONUS Want to see it all working? Check out the gitlab repo with a fully-built example

At the very end of Part 3, I mentioned starting to explore using tape instead of mocha due to a blog post I saw online extolling the virtues of tape’s simplicity. This post goes into what my tests looked like after the switch.

tldr; tape works just fine, and I don’t have to think about it. Tests run sequentially so it makes it easy to perform setup and reuse across tests without worrying when things will run (again, simplicity).

Why the switch?

As stated in the previous blog post, I got really annoyed with mocha’s before functionality not being as easy to use as I expected, for setting file-local setup. Also, the fact that it didn’t necessarily run before describes in the same context was inconvenient enough for me to look elsewhere. Mocha is a great testing framework, but with all the options out there, even such a little annoyance made me look for something else. There were also things like the “ghost” test suite that Mocha creates that just didn’t leave me feeling enthused about using it anymore.

What do the DOM tests look like after switching to tape?

Here’s what a test looks like:

test("obj-creation-dialog component should $emit a created event on successful creation", {timeout: 10000}, t => {
  TestUtil.renderModuleInJSDOM(COMPONENT_JS_PATH, "Component")
    .then(TestUtil.instantiateComponentFromLoadedGlobal("Component"))
    .then(TestUtil.addComponentElementToBody)
    .then(elemInfo => {
      // Set up fake
      var url = "example.com";
      var text = "This is a test";

      // Get the elements for fields
      var urlField = elemInfo.window.document.getElementById("url");
      var textField = elemInfo.window.document.getElementById("text");
      var createBtn = elemInfo.window.document.getElementById("btn-create");

      t.ok(urlField, "url field should be present on off-page element");
      t.ok(textField, "text field should be present on off-page element");
      t.ok(createBtn, "createBtn should be present on off-page element");

      // Set the values on the page
      elemInfo.vm.url = url;
      elemInfo.vm.text = text;

      // Ensure the values were taken up by VM
      t.equal(elemInfo.vm.url, url, "vm.url should have updated to fake url");
      t.equal(elemInfo.vm.text, text, "vm.text should have updated to fake text");

      // Watch for created event to be emitted
      elemInfo.vm.$on('created', obj => {
        t.pass("created event emitted");

        // Ensure that the $emit-ed destroy contains the alert and alert ID we expect
        t.assert( obj.text.should.be.eql(text), "text should match entered value");
        t.assert( obj.url.should.be.eql(url), "url should match entered value");

        t.end();
      });

      // Click on the creation button, should create
      TestUtil.jsdomClick(createBtn, elemInfo.window);
    })
    .catch(t.fail);

});

As you can see, there has been a bunch of refactoring here, a lot of stuff that was reusable went into the TestUtil module, and that’s where the real heavy lifting of calling jspm and jsdom are.

One of the downsides to this approach is that I just can’t seem to set the dom elements value properly just yet (and get the vuejs reactivity system to notice). I probably need to do a bit of a deep dive into the Vue code for how it detects entering data, but luckily I can just edit the VM values directly. I attempted sending keyboard events to the elements and that didn’t work either. At this point, I’m just hoping that Vue’s reactivity is something I don’t have to test, but this is a bit of a blindspot for these tests.

Here’s what output looks like when running the tests overall:

$ make test
./node_modules/.bin/tape app/**/*.spec.js | ./node_modules/.bin/tap-min
[Vue warn]: The data property "alertIdx" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "alert" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "alertIdx" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "alert" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "alertIdx" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "alert" is already declared as a prop. Use prop default value instead.
(found in root instance)
[Vue warn]: The data property "maxAlertCount" is already declared as a prop. Use prop default value instead.
(found in component <alert-listing>)
[Vue warn]: The data property "alerts" is already declared as a prop. Use prop default value instead.
(found in component <alert-listing>)
[Vue warn]: Invalid prop: custom validator check failed forprop "maxAlertCount".
(found in component <alert-listing>)
[Vue warn]: The data property "maxAlertCount" is already declared as a prop. Use prop default value instead.
(found in component <alert-listing>)
[Vue warn]: The data property "alerts" is already declared as a prop. Use prop default value instead.
(found in component <alert-listing>)

  50 tests complete (2s)

./node_modules/.bin/tape app/**/*.spec.e2e.js | ./node_modules/.bin/tap-min

  17 tests complete (19.5s)

Note all the pesky warnings coming from Vue – these warnings are safely ignored. It’s super annoying but it looks like I’ll just have to deal with that output.

As you can see, my E2E tests are seperated (so that if I want to run the unit tests continuously I don’t have to wait ~20 seconds), but they do take quite a bit of time, roughly ~1 second per test.

Was it worth?

Yes, I think it was. Tape is staggeringly simple, and I love tools like that. Creating per-file setup was simple – all I had to do was write a test that did the setup and always passed.