Introducing Luna — JavaScript Testing Done Right

June 05, 2018 0 Comments

Introducing Luna — JavaScript Testing Done Right



I’m sure you are reading this and thinking:

There are already a million JavaScript testing frameworks. Why does the world need another?

That was my first thought as well. I was working on a project and decided I wanted to add some unit tests. I tried AVA. Then I tried Tape. Then Mocha. Then Jest. Three hours later I was right back where I started. Nothing worked out of the box. I was stuck battling with Babel configs, ES6 modules failing to be imported/resolved, external dependencies, testing configs, etc. I grew more and more frustrated.

As someone who has been writing software for quite a while now, if I’m having this much trouble, then I can only imagine what new developers must be going through. I suspect a lot of people give up and end up not writing any tests at all.

Now is the part where readers will chime in:

But sir, clearly you must be a moron. I use those frameworks and they are so easy to set up. It takes a couple minutes to get up and running.

Congratulations. I am happy for you. I’m not saying my experience is shared by everyone. It really depends on the specific project you are working on.

Either way, we should at least be able to agree that all of the different JavaScript build tools make adding tests to your project more difficult than it should be.

Karma, Mocha, Jasmine, Jest, Sinon, Chai, AVA, Babel, Gulp, Grunt, Webpack, Buble, Rollup, jsdom just to name a few.

There is a kaleidoscope of boilerplate code that you have to write before you can write a single test. I counted over 50 configuration options in the Karma documentation and many of those have their own set of sub-options! The fact that Karma is a runner for other unit test runners and that the intro video is around 15 minutes long should tell you everything you need to know.

Most testing frameworks require plugins or other tools to do simple things like code coverage. It may not necessarily be difficult to add, but it is still an extra step you have to take.

It is not easy or even possible in some testing frameworks to test using a browser despite the fact that most JavaScript code is written to run in a browser. Think about that for a second. They suggest you use jsdom, a node tool that simulates the browser DOM on the server side. Of course, that may work in some situations, but shouldn’t tests be designed for the browser by default?

I have been writing a lot of go lately, and something that I really like is how go’s testing is built directly into the language. You can literally create a file, add some tests, and run them without having to install any external dependencies.

This is part of what inspired me to create Luna. I was looking for a unit testing framework that just worked and did not require any crazy configuration or external libraries. I wanted to be able to add a test function anywhere in my code, run the test, and see the result.

As a result, Luna has no configuration at all.

In order to achieve this it does make some assumptions about the environment you are working in. Your tests have to be written as ES6 modules, and your code should be too. Luna does not transpile any source code with the exception of JSX. This means if you are using Coffeescript or Typescript you will be out of luck right now.

Luna searches for any functions whose names begin with test that are exported from whatever files and directories you tell it to look in. It then uses rollup’s API to dynamically create a test running bundle in memory. Each test function is called and passed an instance of an object with a single assert method. The results are communicated back to the main process using console logs.

This means everything is self contained and there are no global variables required. In addition, you do not need to import any custom libraries to run your tests since the test functions themselves get pulled out of your code.

  • Because of the way Luna works you can include test functions anywhere you want including in your source files themselves. Note: This is only recommended if you are using tree shaking when you build your production bundles to strip out the test code.
  • Browser testing is built in and runs by default. It uses puppeteer to run in a recent version of Chromium.
  • It supports concurrency using a backpressure queue to allow tests to run in parallel. This is useful if some tests are CPU intensive or take a while to run.
  • It generates code coverage reports automatically without having to install any additional libraries (pass the --no-coverage flag to disable them).
  • Luna will translate and apply source maps automatically to code coverage reports and stack traces when your tests have an error even though Puppeteer does not support them.
  • The assert function used by Luna is inspired by power-assert so if your test fails, you can see exactly which part of the statement failed instead of a generic message like: Failed asserting that false is true. Here is an example output for a failed test:
❌ testGetData
25 t.assert(data3.message = 'Something should be false');
26 |
"Something should be true"
  • It supports comparisons to arrays and objects without having to use any custom syntax (just use or != for the comparison):
t.assert(list == ['one', 'two', 'three']);
  • It compiles JSX code automatically into React.createElement syntax.
  • The tests for Luna are run by Luna. How meta!

I am certain that it is not perfect because there is no silver bullet that will work for everybody especially given the state of JavaScript development today, but I believe it is a step in the right direction, and I hope other people think so too and find it useful!

You can find out more and read the full documentation on GitHub.

Also be sure to check out the project introduction page.

Tag cloud