Kasra Khosravi Founder @ FeedbackOnSite.co

How to use uvu: A fast and lightweight test runner

4 min read 1289

How to use uvu: A fast and lightweight test-runner

uvu (short for ultimate velocity, unleashed) is considered to be one of the fastest and most lightweight test runners for Node.js and browsers. Its main features include individually executing test files, supporting asynchronous testing, supporting native ES modules, compatibility with browsers, outstanding lightweight size, familiar API, and remarkable performance. This blog post will cover the usage of uvu, its comparison with two other popular test runner libraries called Jest and AVA, and why and when to use it for testing.

Why use uvu?

First of all, uvu supports asynchronous testing which is one of the common advantages that some testing libraries support. It assists in determining that the code which is being tested has completed the test process before it moves towards the next test. The prime objective of an asynchronous (async) function is just to clarify the syntax that is mandatory to consume promise based API’s. In asynchronous testing, a method will be used like callback or promise which will determine the completion of a test process.

Another main feature is browser compatibility. At first, it was an issue that uvu was not compatible with the browsers but this was resolved by performing a little modification with the process file. You can find the discussion regarding the issue resolution here. So even if you are having any kind of issues with the browser compatibility you can check this link for a better understanding and sort out your problem.

Using uvu

Using uvu is simple and works like this:

// tests/demo.js
// Source: https://github.com/lukeed/uvu

import { test } from 'uvu';
import * as assert from 'uvu/assert';

test('Math.sqrt()', () => {
  assert.is(Math.sqrt(4), 2);
  assert.is(Math.sqrt(144), 12);
  assert.is(Math.sqrt(2), Math.SQRT2);
});

test('JSON', () => {
  const input = {
    foo: 'hello',
    bar: 'world'
  };
  const output = JSON.stringify(input);
  assert.snapshot(output, `{"foo":"hello","bar":"world"}`);
  assert.equal(JSON.parse(output), input, 'matches original');
});

test.run();

Now what you need to do is just to execute this test file:

# via `uvu` cli, for all `/tests/**` files
$ uvu -r esm tests

# via `node` directly, for file isolation
$ node -r esm tests/demo.js

The point to be noted about the above command lines is that –r esm is only specified for legacy Node.js modules since the Ecmascript (ES) modules are deposited to Node.js versions >12.x. By default, .js and .cjs files are treated as Common.js, and .mjs file extensions are only the ones that would be served as Ecmascript Modules (ESM).

The above example can be considered as the simplest method through which Node.js will load ES modules and grant them to import any module when required. Also, you can load modules in some other ways as well that are shown below.

There are also other ways through which Node.js will load ES modules. These methods include type, module and esm package procedures. Here are the complete guides of these methods:

Main uvu module

The main uvu module will assist regarding the tests or test suits(series of individual tests that are related to a certain functionality in the code) that are required for all the uvu tests. The users have the option available here whether to choose uvu.test or uvu.suite. Through uvu.suite one can grasp numerous additional perks like testing multiple files at once while one should choose uvu.test if thinking about testing a single file only (technically uvu.test is an unnamed test suite).

uvu.suite(name: string, context?:T)

You can have as many suites as you want in the same file but it’s necessary to call suites run for each suite to be added to uvu’s queue. This just returns a suite along with creating a new suite. The name here corresponds to the name of the suite and is of type string. This will combine all console output together and will suffix the name of any test that fails. The context of the suite has an empty object as default value and is of any type. This will be passed to every test-block and hook inside the suite.

We made a custom demo for .
No really. Click here to check it out.

uvu.test (name: string, callback: function)

If there is a requirement of testing only one file you can import this uvu.test. The name here obviously denotes the name of the test and is of type string and the callback here is consisted of the test code and is of type promise<any> or function<any>. The callback may be asynchronous and return values that are even though abandoned.

Methods

Creating

Each and every suite can be called and like this suite(name, callback).

Running

In order to run a suite, one should add the suite to uvu test queue and use suite.run().

Skipping

Skipping a suite can assist in missing an entire test block as suite.skip(name, callback).

Additional methods

For organizing an environment or establishing fixtures an ideal case would be to request the given callback before the beginning of the suit in the following way suite.before(callback).

Also for finishing an environment or fixture an ideal case would be to request the callback after the completion of the suite in the following way suite.after(callback).

Here is a sample code of the above description:

import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import * as dates from '../src/dates';

const Now = suite('Date.now()');

let _Date;
Now.before(() => {
  let count = 0;
  _Date = global.Date;
  global.Date = { now: () => 100 + count++ };
});

Now.after(() => {
  global.Date = _Date;
});

// this is not run (skip)
Now.skip('should be a function', () => {
  assert.type(Date.now, 'function');
});

// this is not run (only)
Now('should return a number', () => {
  assert.type(Date.now(), 'number');
});

// this is run (only)
Now.only('should progress with time', () => {
  assert.is(Date.now(), 100);
  assert.is(Date.now(), 101);
  assert.is(Date.now(), 102);
});

Now.run();

Why uvu is better than Jest and AVA

First, let’s take a look at a comparison of test runners’ times. Below are the results of the sample test (achieved by testing the sample code which is present here) ran by few test runners with two timings. The first value is the total execution time of the whole process and the other value is the self-reported performance time only if it is known:

~> "ava"   took   594ms ( ???  )
~> "jest"   took   962ms (356 ms)
~> "mocha" took   209ms (4 ms)
~> "tape"   took   122ms (  ???  )
~> "uvu"   took    72ms (1.3ms)

It’s obvious from the above result that uvu is the fastest option among its competitors.

Now let us talk a bit about the features comparison as well:

  • AVA and uvu both provide asynchronous testing while Jest doesn’t
  • Jest and uvu allows you to integrate into other apps quite easily while AVA, being a minimalistic testing library, doesn’t provide such integration as the other two do
  • AVA containing just a simple API requires installing an additional library for mocking support while Jest and uvu have a wide range of API not requiring the user to include additional libraries to have numerous features support

Conclusion

There has always been apprehension regarding the performance of test runners, but the features that uvu has provided have proved to be one of the finest ones. It’s just like an all-in-one library for anyone worried about browser compatibility, high-speed testing, supporting native ES modules, asynchronous testing, and individually executing test files from a single library. So whenever you are anxious about all these things you just need to switch to one solution and that’s uvu.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Kasra Khosravi Founder @ FeedbackOnSite.co

Leave a Reply