Javascript Testing

Testing is essential to development. I've obviously used testing libraries such as Mocha, Chai, Ava, Tape, etc. But how would you go about writing one yourself? Especially how would you build a test framework using that same framework to test itself? I got to thinking, just what would a test runner or 'test harness' consist of? I thought the basics would be:

  • It should be able to look for test file/s and execute tests within
  • It should be able to make assertions within an executed test
  • It should be able to report successes and failures of these assertions

So we would have a command line tool. We would run and record tests and their assertions and record results into a report that is delivered as the conclusion of the tests.

So I went ahead and created a testing framework called Testu, and this is what I learned.

Harness

The term harness, or 'test framework', is the thing that keeps track of the tests and sends the result to the reporter.

Assertions

Assertions are statements of facts or beliefs. So they are binary; that an assertion is either correct or wrong. There are many styles of assertions, some prefer a string like method such as expect(value).to.be.ok() or simply assert(condition).

General assertions:

  • 'assert', 'ok', 'isOk': truthy
  • 'assertNot', 'not', 'notOk': falsy
  • 'throws', 'throw': expect an exception to be thrown
  • 'doesNotThrow', 'notThrow': will not throw an exception
  • 'equal', 'equals', 'isEqual', 'is', 'strictEqual', 'strictEquals', 'strictIs', 'isStrict', 'isStrictly': equality condition
  • 'deepEqual', 'deepEquals', 'isDeepEqual', 'isDeep', 'strictDeepEqual', 'strictDeepEquals': object/array equality condition
  • 'notEqual', 'inequal', 'notEqual', 'notEquals', 'notStrictEqual', 'notStrictEquals', 'isNotEqual', 'isNot', 'doesNotEqual', 'isInequal': object/array not equal
  • 'notNull', 'isNotNull': specific type assertion

Helper assertions:

  • 'isAbove', 'above', 'greaterThan', 'over': numeric value above
  • 'isBelow', 'below', 'lessThan': numeric value below
  • 'isAtLeast', 'atLeast', 'greaterThanOrEqual': numeric value less than or equal
  • 'isFalse': falsy value
  • 'isTrue': truthy value

Included on the assert function is 'expect', so you can destructure and use the expect format:

test('using expect' ({ expect }) => {
  expect(true).to.be.ok();
  expect(0).to.equal(0);
});
  • 'equal': equality test
  • 'ok': truthy test

Reporter

I decided to use TAP (test anything protocol) for the default reporter.

Mocks and Stubs

I haven't included any of these in Testu (yet). However they are important enough to be covered.

Stub
A function that returns known dummy data
Spy
A stub with meta data such as what were the arguments or how many times the function was called
Mock Objects
simulate behaviour with simple dummy objects
Fixtures
dummy data, could be DOM, JSON, set up and torn down
xvfb
x virtual frame buffer

In computer science, test stubs are programs that simulate the behaviors of software components (or modules) that a module undergoing tests depends on Wikipedia

Things like Superagent can be used for integration testing your app. Ideal when using things like Mockgoose, a Mongoose mock etc.

How To Write A Unit Test

People bang on about unit tests, and quite right too. However, nobody really shows you how to write them. Like I say, devs bang on about 'Oh do you know how to unit test? I do and I'm great'. Yet they never show you. Same as tutorials, straight into coding solutions. To begin with, a unit test should be ...

  • Relevant
  • Repeatable
  • Fast
  • Isolated (test one thing only)

Fail, pass, refactor

The process is fail, pass, refactor. The first test you want to write should fail, so the first thing I like to do is just test that there is a function (if it is a function you are testing that is).

const sort = require('sort');
test('sort', assert => {
  assert(typeof sort === 'function', 'should be a function');
});

As the tests get more specific, the code gets more generic

Uncle Bob can explain this a lot better than me :-)

Code coverage

A great tool is code coverage. It comes bundled with test frameworks such as Jest, or you can install istanbul or nyc code coverage tools. 100% coverage is not essential.

References