Let’s get started with Mocha

Be forewarned this post is going to be a brain dump as I work through this information.

I have never wrote Mocha before, and to be honest I have never been good about TDD. I always write my code and then go back and write my tests. This is mainly because I am not always sure what I expect to get out of my code until it’s too late. However, with this project I am trying to push myself to work on myself just as much create something in my head from start to finish.

So I got started with knowing I need to have a login form. Since I am working in Vue JS 3 I set it up to have mocha as my unit tests, and it came with an example for the Login HelloWWorld file. So I copied that and started replacing everything for a login form.

import { expect } from "chai";
import { shallowMount } from "@vue/test-utils";
import LoginForm from "@/components/LoginForm.vue";

describe("LoginForm.vue", () => {
  it("renders username field when passed", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.find("input.email")).to.include();
  });
  it("should have props for email and password", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.props("email")).to.defined;
    expect(wrapper.props("password")).to.defined;
  });
});

My idea here is that my login form component should contain a username and password. Right now the test is not working because I don’t have the component created as a file to even include in the test. So I am going to go fix that really quick.

<template>
  <div class="login">
    <form action="/login" method="post">
      <input type="email" name="email" value="" />
      <input type="password" name="password" value="" />
    </form>
  </div>
</template>

<script>
export default {
  name: "LoginForm",
  props: {},
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss"></style>

And the errors turned out better than I had hoped to be honest (given I don’t know what I am doing with mocha)

MOCHA  Testing...

 LoginForm.vue
   1) renders username field when passed
   2) should have props for email and password

 HelloWorld.vue
   ✓ renders props.msg when passed


 1 passing (128ms)
 2 failing

 1) LoginForm.vue
      renders username field when passed:
    Error: Cannot call Symbol(Symbol.toStringTag) on an empty DOMWrapper.
     at Object.get (dist/js/webpack:/node_modules/@vue/test-utils/dist/vue-test-utils.esm-bundler.js:1649:1)
     at Object.typeDetect [as type] (dist/js/webpack:/node_modules/type-detect/type-detect.js:238:1)
     at Proxy.include (dist/js/webpack:/node_modules/chai/lib/chai/core/assertions.js:476:1)
     at Proxy.chainableMethodWrapper (dist/js/webpack:/node_modules/chai/lib/chai/utils/addChainableMethod.js:113:1)
     at Context.<anonymous> (dist/js/webpack:/tests/unit/example.spec.js:8:1)
     at processImmediate (node:internal/timers:466:21)

 2) LoginForm.vue
      should have props for email and password:
    Error: Invalid Chai property: defined. Did you mean "undefined"?
     at Object.proxyGetter [as get] (dist/js/webpack:/node_modules/chai/lib/chai/utils/proxify.js:75:1)
     at Context.<anonymous> (dist/js/webpack:/tests/unit/example.spec.js:12:1)
     at processImmediate (node:internal/timers:466:21)



MOCHA  Tests completed with 2 failure(s)

ERROR  mochapack exited with code 2.

:’( and of course I started off with a silly mistake of naming the test with username and then expecting email. So I’ll fix that, but then I need to find out why it’s got an empty DOMWrapper. Any time I tried to search mochajs for documentation I only really got instructions on how to set it up, but I saw something mention chaijs when I searched “mochajs to include” so I looked into that and found that mochajs uses chaijs to handle assertions.

Once I did that I was able to quickly find that I was not using the correct selector, and so I needed to switch that and the insertion.

it("renders email field when passed", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.find("#email")).to.exist;
  });

This one now tests correctly and I can move onto other tests. These tests took me a minute to figure out because I was not expecting this to be the use case too, but sure enough exist works on them as well.

it("should have props for email and password", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.props("email")).to.exist;
    expect(wrapper.props("password")).to.exist;
  });

The final products look like the following:

<template>
  <div class="login">
    <form action="/login" method="post">
      <input type="email" name="email" id="email" value="" />
      <input type="password" name="password" id="password" value="" />
    </form>
  </div>
</template>

<script>
export default {
  name: "LoginForm",
  props: {
    email: String,
    password: String
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss"></style>
import { expect } from "chai";
import { shallowMount } from "@vue/test-utils";
import LoginForm from "@/components/LoginForm.vue";

describe("LoginForm.vue", () => {
  it("renders email field when passed", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.find("#email")).to.exist;
  });
  it("should have props for email and password", () => {
    const wrapper = shallowMount(LoginForm);
    expect(wrapper.props("email")).to.exist;
    expect(wrapper.props("password")).to.exist;
  });
});

Next steps

The next steps will be to incorporate test to make sure that logging in will actually work as expected.




Hero Photo by Koushik Pal on Unsplash