DEV Community

Cover image for Be Smart, Write Parameterized Tests with Jest!
Tine Kondo
Tine Kondo

Posted on • Updated on

Be Smart, Write Parameterized Tests with Jest!

Have you ever feel like that extra it or test block you've just added inside your describe test block, looks exactly like the one you wrote before ? 🤔

Recently, while unit-testing one of my latest OSS projects ( @nxrocks/nx-spring-boot: a plugin to generate and integrate a Spring Boot project inside a Nx workspace), I asked myself that same question.

My use case

I needed to test that my plugin was working perfectly for the two build systems available for Spring Boot projects (Maven and Gradle), as well as for generating either application or library projects.

I had already tested the correct generation for Maven-based applications:

So 3 more combinations remained to be tested:

  • Generation of Maven-based libraries
  • Generation of Gradle-based applications
  • and Generation of Gradle-based libraries

The naive solution: duplicated test blocks

So at first, I copy-pasted that first test to make the others three. The complete test suite was:

It was working fine of course... but felt terribly wrong. The 4 tests are looking almost exactly the same, except for a few parameters that change.
I wasn't comfortable with this duplication as it makes refactoring harder and gives a huge kick in the n**ts🌰 to the DRY principle.

Coming from a Java background, I was aware of a technique called parameterized testing with Junit (the most used testing library in Java), which allows executing a single test method multiple times with different parameters.

That was exactly what was needed here. So I asked myself:

does the same kind of library exist in Javascript?

A quick search on Google later, I found what I was looking for: jest-each

A parameterised testing library for Jest inspired by mocha-each.

The optimal solution: parameterized tests!

jest-each (which is already integrated into the Jasmine's it/test/describe provided by Jest, so no additional installation required) allows you to provide multiple arguments to your test/describe blocks, which results in the test/suite being run once per row of parameters.

This is how the previous test suite could be rewritten:

Soooo much better right?! Short and concise 😉.

How does it work?

Let's break it down a little bit:

1 - In the above code block (lines 4-9), using a tagged template provided by the library, we can express the parameters of our test, in this cool table-like style.

it.each`
    projectType      | buildSystem         | buildFile         | wrapperName
    ${'application'} | ${'maven-project'}  | ${'pom.xml'}      | ${'mvnw'}
    ${'application'} | ${'gradle-project'} | ${'build.gradle'} | ${'gradlew'}
    ${'library'}     | ${'maven-project'}  | ${'pom.xml'}      | ${'mvnw'}
    ${'library'}     | ${'gradle-project'} | ${'build.gradle'} | ${'gradlew'}
`
Enter fullscreen mode Exit fullscreen mode

Note: The first row expresses the parameters, the following rows express the different values they can take

2 - In the test's (or it's) description, we can reference the parameters (the headers in the above table) using the syntax $paramName, which allows a dynamic test description.

3 - We reference the parameters in our test body, by providing them as an object parameter of our test function ({ projectType, buildSystem, buildFile, wrapperName }).

("should download a spring boot '$projectType' build with '$buildSystem'", async ({ projectType, buildSystem, buildFile, wrapperName }) => {
  // your expectations here...
}
Enter fullscreen mode Exit fullscreen mode

That's it!

Happy Testing! 👩🏾‍💻👨🏾‍💻

Top comments (5)

Collapse
 
misobelica profile image
Mišo

jest in case you didn't notice there is one similar library :)

Collapse
 
tinesoft profile image
Tine Kondo • Edited

Hi Mišo,

Thanks for sharing! Different library, but same base idea: parameterized testing!
I personally find jest-each to be more readable through... But hey, opinionated choice!

Collapse
 
0916dhkim profile image
Danny Kim

I guess it will be less tidy, but can't you put it calls inside a for-loop?

Collapse
 
bponomarenko profile image
Borys Ponomarenko

Personally I prefer to use Jest own built-in ‘test.each()’ api for this purpose, as it doesn’t require any additional packages.

Collapse
 
tinesoft profile image
Tine Kondo

Hi Borys,

Thanks for your input! I wasn't aware of the existence of each() right from the it/test objects from Jest Jasmine! Plus, it is exactly the same API! In fact, the feature actually comes from jest-each package: github.com/facebook/jest/tree/mast...

So no need to install the package separately!

I will update the article accordingly.

Cheers!