
A Simple JavaScript test framework from scratch
In this article, I build a simple JavaScript framework from scratch. The framework will then be used to unit test itself.
The goal is to be able to run this:
Let’s get started. The full source code is here and tests are here.
Create a new file called index.js
. The entire source code will live inside here. Let’s start from describe
.
Describe
The describe
function signature is fn(description, callback)
. The first argument is simply a string describing the block, and the callback
contains the actual test code to execute. It would be also be nice to print out the description when the test executes.
Relatively straightforward! Another nice part of this we can have arbitrarily nested describe
blocks. For example:
Surprisingly simple. Moving on…
it
it
is actually easier to implement than describe
, at least for this example. Why? Let’s take a look at the function signature. The first argument is a message, and the second is the callback:
fn(description, callback)
… which is identical to describe
. Let’s just go ahead and use describe
!
Now we just need to implement expect(....).toBe(...)
.
expect
Let’s start with expect
. The function signature is as follows:
expect(value).toBe(value)
Since we want to chain .toBe
, expect
must return an object that has a toBe
property that is a method.
Start of by defining the expect
method:
Now we can type expect(1)
. We just need an object with a toBe
property, that does the comparison between the value passed to expect
and the value passed to toBe
.
matchers
The object returned by expect
will contain at least one matcher, toBe
, and possibly more in the future. It looks like this:
exp
is the expected value, which is passed to expect
. We need to pass it into matchers
, to be able to make the comparison. toBe
receives the assertion, which is what we expect to equal value passed to expect
.
toBe
is performing a strict comparison using ===
. This means toBe
can only be used for primitives, such as String
, Number
and Boolean
.
This is all we need to be able to run the example code from the start of the article! The full source code with example as follows, and can be pasted into a single index.js
and run using node index.js
.
I am exporting the methods at the bottom of the file, so I can require
and test them.
Testing the testing framework
As promised, we will test the framework using itself. I will write the tests in a file called index.test.js
, which can be run using node index.test.js
.
First, a test on describe
.
Testing describe
describe
… actually doesn’t do much. It simply console.log
a value, and executes the callback
. We could mock console.log
and ensure it is called, but that isn’t very interesting. Let’s focus on making sure the callback is called.
We start of by requiring all the functions we will test (and need to write the tests). We simply declare a noop
, or no operation function that increments a value each time is it called — this is how we will assert that describe
is calling the callback.
Testing describe
is now as simply as asserting executes
has indeed increments, which reflects that the noop
callback did indeed execute.
it
It just calls describe
, and does nothing different, so testing is isn’t very interesting.
expect
expect
is also very straight forward. expect
returns an object, with a toBe
property that is a function
.
matchers.toBe
toBe
is as simple to test as everything else. It should return true
for when primitives are equal, such as 1 === 1
, and false for 1 === 2
.
Writing false
is exactly the same, so I won’t include it.
Conclusion and Improvements
index.js
, not including whitespace and exports
, is less than 30 lines of code! This isn’t a very full featured framework by any means, but for just 30 lines, it’s relatively powerful. Adding more matchers as as simply as adding extra methods to matchers
.
Building isEqual
, isTruthy
and are trivial. Add more advanced matchers like toHaveBeenCalled
, and other test features like spies and mock functions are some improvements I’d like to implement, as well as better error handling and logger.
✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.