DEV Community

leanminmachine
leanminmachine

Posted on

Test Driven Development 101 and JS testing

Test Driven Development Cycle

TDD Cycle:

  1. Write a test case which will obviously fail in the beginning. Do not have more than one failing test case at a time. Implement the simplest algorithm first, then generalise it when you identify some patterns.
  2. Write code that makes test case pass
  3. Refactor code on code base. Do not refactor when your tests are failing. Make test cases pass first.

I’m honestly still trying to get the hang of testing and I am still trying to familiarise myself with js in general.

Writing tests in Jest

Setting up is pretty straightforward.
Just npm install jest.
Remember to change in package.json,

  "scripts": {
    "test": "jest"
  },
Enter fullscreen mode Exit fullscreen mode

Name test file [file name].test.js
First, we write our test.
The test would require the main module.
We can assign it to a variable name.
For example:

const arabicToRomanNumbers = require(“./arabicToRomanNumbers”); 
Enter fullscreen mode Exit fullscreen mode

Following that, we can start writing our test.

The first test would be something like,

test(“I is 1”, () => {
expect(arabicToRomanNumbers(“I”)).toBe(1);
});
Enter fullscreen mode Exit fullscreen mode

Name file name [file name].js
Main js file: Just write the function.

function arabicNumber(string) {
if (string == “1”) {
return 1;
}

export.modules = arabicNumber;
Enter fullscreen mode Exit fullscreen mode

BTW: export class MyClass doesn’t work for nodejs. I have to use the module.exports keyword :( Or use babel npm package to transpire ES6 into commons target.

You can write all your function declarations first and then export them in an object:

function bar() {
   //bar
}

function foo() {
   //foo
}

module.exports = {
    foo: foo,
    bar: bar
};
Enter fullscreen mode Exit fullscreen mode

There's no magical one-liner though, you need to explicitly export the functions you want to be public.

var exported = {
   someFunction: function() { },
   anotherFunction: function() { },
}

module.exports = exported;
Enter fullscreen mode Exit fullscreen mode

Writing tests in mocha:

Somehow, I have to name my test file test.js. Not sure why yet.

Need to also declare assert.
const assert = require('assert');

Otherwise, the structure is pretty similar.
Write a function, and then use the function during the test itself.

describe('romanise', () => {
    it('should be less than 4000', function () {
        const result = romanise(4000);
        assert.equal(result, 'invalid input');
    });

// write more it and asserts here.

});
Enter fullscreen mode Exit fullscreen mode

Oldest comments (5)

Collapse
 
kayis profile image
K

"export class MyClass doesn’t work for nodejs. I have to use the module.exports keyword"

Yes, seems like ES modules are experimental in node 10.

They have to be manually activated with the --experimental-modules command line parameter and the module needs the .mjs extension :(

Collapse
 
cstroliadavis profile image
Chris

You can now use the esm module from Dalton to assist with this. "npm init esm"

Collapse
 
akaustav profile image
Ameet Kaustav • Edited

Hi @leanminmachine ,
First of all, congratulations on your first post in dev.to. Your article is definitely inspiring.

However, I found a few potential flaws in your article which got me a bit confused. Please consider the following as positive feedback to improve the article for future readers - I do not aim to discredit your work. Feel free to accept/reject each proposal.

1. Arabic vs Roman Numerals

Arabic Numerals are: 0, 1, 2, 3, 4, ...

Roman Numerals are: N, I, II, III, IV, ...

In your jest based test file, the function being tested is named arabicToRomanNumbers. From the name, I infer your function takes in an Arabic numeral (e.g. 1) and converts it into a Roman Numeral (e.g. I). However, in your test case, you seem to be passing in a roman numeral (I) and expecting an Arabic numeral (1). That seemed confusing to me.

My proposal: Maybe change the function name to romanToArabicNumbers. Or modify your test case to accept 1 and return I.

2. module.exports vs exports.module

In your main JS file, the following line appears at the end of the file:

export.modules = arabicNumber;

I DO NOT believe that there is a global export.modules object available in NodeJS or in ES5/6/7.

My proposal: If you're writing your file for NodeJS, consider changing to

module.exports = arabicNumber;

or simply

exports = arabicNumber;

Otherwise, if you're writing your file for ES6 style exports, consider changing to the arabicNumber function definition to the following.

export default function arabicNumber(...) {
  ...
}

Like @kayis pointed out, you may also be able to use the experimental ECMAScript Module (ESM) style exports in NodeJS 10.0 or above with the .mjs extension on your main file. Read more here.

3. Inconsistent Function Names

From the require statement in the test file, I assumed the main file is named arabicToRomanNumbers.js. It assumes that the default exported member is a function.

However, inside the main JS file, the default exported function is named arabicNumber. It does not match the function which you are trying to test in your jest test file.

My proposal: While this is not a big deal, you may want to consider keeping function names references consistent throughout your article. It really helps readers new to the Javascript module system.

4. Possible Typo in Code

Your main arabicNumber function contains few flaws:

  1. It's missing the closing braces for the function body.
  2. It compares a string 1 and returns numeric 1. Perhaps you should compare with string I.
  3. In my opinion, the parameter name could be improved.

Your code:

function arabicNumber(string) { // 3. bad name for the parameter
if (string == 1) { // 2. Possible typo
return 1;
} // 1. Missing closing braces for function

My proposal: You might want to consider doing something like the following (the logic in this example is bad - and can be improved, but it helps with your test case):

function arabicNumber(roman) {
  let arabic = NaN;

  // Logic can be improved - for demo purposes only
  if (roman === 'I') {
    arabic = 1;
  } else if (roman === 'II') {
    arabic = 2;
  } else ...

  return arabic;
}

@akaustav

Collapse
 
leanminmachine profile image
leanminmachine • Edited

Thank you for the comment & clarification!

I didn't expect this to blow up publicly though so I didn't really scrutinise it before posting, just wanted to post on here to keep track of what I've learnt so far :D Probably I'll edit it, and repost again :)

Collapse
 
akaustav profile image
Ameet Kaustav

Great. Looking forward to your edits. Good luck!