Testing w/ Vitest w/o Vite
Published: 2023-03-13 02:18:55

Vitest is an ultra fast and lightweight Unit Test Framework that extends the Jest API. Because of this, it is has a super intuitive set up and familiar syntax. In the following blog I will show you how to get it up and running in a project that isn't even using the Vite bundler! I know a lot of us know and love webpack, so if you're like me (and have already dedicated a hundred hours to learning how to use webpack...) and you don't necessarily want to jump ships to a different bundler, but, you _do_ want to use something more modern than Jest for your testing; than this blog should be helpful in explaining how to do so. Let's get started:

Along with the qualities I mentioned above, the fast and lightweight aspects of Vitest, there are other reasons we will enjoy using Vitest. A couple of these reasons are the following: The first, and my favorite, is its out-of-the-box compatibility with ES6, JSX and TypeScript; and it's easily configurable reporting and watching. I will touch on the latter of those two a bit later.

## SETUP
If you _are_ using Vite, this part will truly be a breeze. If you are using `npm` simply run this command in your terminal:

```javascript
npm install -D vitest
```

If you are using Vite then you will already have a `vite.config.ts` file and Vitest will use that configuration file to execute alias configuration, as well as, whatever plugins are needed. This will happen automatically with installation. However, and this is what we will mostly focus on, if you are using a different bundler than you will want to create a `vitest.config.ts` file that we will configure if we need specific changes to be made to our testing framework. If you happen to be using Vite and you create a `vitest.config.ts` file it will take priority over the `vite.config.ts` file. Either way, for developers using webpack or rollup or something other than Vite we will be creating a file that looks like this:

```javascript
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true, // <-- **
    coverage: {
      reporter: ['text', 'html'], // <-- ***
  },
});
```
** --> This line will allow us to use the methods from the Vitest library without specifically importing which ones we need (for those who are familiar with Jest if will behave similarly this way).
*** --> This `coverage` object and the `reporter` array is where we can pass values to influence how the tests display their responses. In this case, I have set the reporter to return and `html` file which we can access with our method of choice in our browser. This will make a very human readably interface that is good for teams or collaborator who want to share testing.

Next, we head over to our `package.json` to add our script:

```javascript
// package.json
/*...*/
"scripts": {
   "start": ...,
   "build": ...,
   "test": "vitest --coverage" // <-- **
   },
/*...*/
```
** --> This `--coverage` flag enables our modifications from our config file to be applied.

With that script created we can simply enter `npm run test` to fire our tests. The `--watch` flag you may be familiar with is applied to Vitest by default so we don't have to include it in our script. 

## TESTING
With that simple set up complete we can now write and execute some tests! I will include a couple example tests and you will notice how familiar they look. But, first, we need some code to test. Here is an example file called: `sum.ts`

```javascript
// sum.ts
export default function sum(...numbers: number[]) {
  return numbers.reduce((acc, cur) => acc + cur, 0);
};
```

It is a extremely simple function that is just a reducer. It will work for our purposes as I can pass 1 in instead of 0 to make the test behave differently than our tests would expect if I want to see a what a failing test looks like.

Now that we have some code, let's write some tests!

```javascript
import sum from './sum';
import { describe, expect, it } from 'vitest'; // <-- **

describe('#sum', () => {
  it('returns 0 with no numbers input', () => {
    expect(sum()).toBe(0);
  });

  it('returns same number with one number input', () => {
    expect(sum(2)).toBe(2);
  });

  it('returns sum with nultiple numbers input', () => {
    expect(sum(1, 2, 3)).toBe(6);
  });
});
```
** --> we do not need this line if we pass the `globals: true` property to our `vitest.config.ts` file like we did above, but this is the best practice as it exposes intention and explicit usage.

You probably recognize this syntax as it is largely the same as Jest and designed to be human-readable. The test-naming convention is to put a hasthag `#` in front of the string that represents the name of the function we are testing, in this case: `'#sum'`. Following that, we pass a callback as our second argument. This callback will contain the `it` invocation which takes a string which describes what we will expect and another callback. Inside the `it` callback we invoke our imported `expect` method and the `toBe` method which is available without explicit importing. `expect` takes what we want to test as an argument, in this case an invocation of our `sum()` function with a given argument. Then, `toBe()` gets invoked and is passed what we want to see as the result of our tested function. We can then run our test script and see if our function behaves appropriately! 


![Passing Vitest Tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0h381v2jxo15v1zhvor.png)

If you don't already have `c8` installed running our Vitest script with the `coverage` options we added will prompt you to do so. Once you have it installed we will see these results (the bottom added by the "text" value added and `coverage` directory added to our root containing the aforementioned `index.html` coverage file for serving. 

As I mentioned above, if I change the 0 to a 1 in my `sum` function my tests will fail and this is what it will look like:


![Vitest Failing Tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1lfo6myg6zezhttv15q.png)



As you can see, the results are clear and easy to comprehend and with a blistering fast response of 21ms and 30ms it is really a clear successor to its ancient testing framework associates. So, with this information under your belt, go give Vitest a try! I have found it very intuitive and rewarding to integrate into my projects. Happy coding!