Please give this post a 💓, 🦄, or 🔖 if it you found it interesting!
One challenge we often face when creating apps is combinatoric complexity. Today, we're going to use a handy helper npm package I created to list all possible permutations of variables we're interested in. This can be especially handy for generating tests for every possible data scenario!
The Problem
Let's say we have an app that has has some user-set color
value, a variable that indicates whether the user is an admin
, and a light
or dark
theme mode
.
The following represents the possible values for each variable:
color = "red" | "blue" | "green"
admin = true | false
mode = "light" | "dark"
This assumes our possible values for color
are "red"
, "blue"
, or "green"
, our possible values for admin
are true
or false
, and our possible values for mode
are "light"
and "dark"
.
We'd like to test our app using each possible combination of these variables. In this case, there are 3 x 2 x 2 = 12 combinations. We could write out all these test cases individually, but that would be a pain. Also, in a real application, you would likely end up with many more than 12 combinations.
Using Combinate
Let's solve this problem with the combinate
package I created!
First, let's get combinate
install. We can do this with npm
or yarn
.
npm i combinate
or
yarn add combinate
Next, let's create an object that represents all the possible options for each variable:
const options = {
color: ["red", "blue", "green"],
admin: [true, false],
mode: ["light", "dark"],
}
Finally, we just have to pass this to our combinate
function, and we'll get an array of all of the possible combinations! let's see it in action.
import combinate from "combinate";
const options = {
color: ["red", "blue", "green"],
admin: [true, false],
mode: ["light", "dark"],
}
const combinations = combinate(options);
console.log(combinations);
/*
[
{"admin": true, "color": "red", "mode": "light"},
{"admin": true, "color": "blue", "mode": "light"},
{"admin": true, "color": "green", "mode": "light"},
{"admin": false, "color": "red", "mode": "light"},
{"admin": false, "color": "blue", "mode": "light"},
{"admin": false, "color": "green", "mode": "light"},
{"admin": true, "color": "red", "mode": "dark"},
{"admin": true, "color": "blue", "mode": "dark"},
{"admin": true, "color": "green", "mode": "dark"},
{"admin": false, "color": "red", "mode": "dark"},
{"admin": false, "color": "blue", "mode": "dark"},
{"admin": false, "color": "green", "mode": "dark"}
]
*/
Using in a Test
Getting all combinations is great and all, but what's actual use case for doing this?
One way I've used this is frontend storyshot generation using a tool like Storybook. Using Storybook in conjunction with combinate
, you can generate visual tests for every possible combination quickly.
A super barebones example, if you're familiar with Storybook, would look something like this:
// Get all combinations
const options = {
color: ["red", "blue", "green"],
admin: [true, false],
mode: ["light", "dark"],
}
const combinations = combinate(options);
// Create main story section
const navbarStories = storiesOf('Navbar', module);
// Add a story for each combination
combinatons.forEach(({color, admin, mode}) => {
navbarStories.add(`${color} - ${admin} - ${mode}`, () => (
<Navbar color={color} admin={admin} mode={mode} />
));
})
Conclusion
Hopefully this little utility saves you some time writing our different app state combinations for things like testing! Would love to hear what you think!
Top comments (7)
A handy utility, but I would just point out that, depending upon the range of potential inputs/values, devs should think very carefully about whether they want to - or whether they even should - test all possible inputs. The theoretical, brute-force approach is to say, "I'll write the best code by testing ALL THE THINGS!!! But you can end up with unit tests that take 15 minutes to run on every save. And for most functions, it's sufficient to say, "There are a million possible combinations that can be input to this function, and once I see that a few hundred combinations have passed, it's reasonable to assume that the function is sound."
Problem cases like this make me think, how often should I resort to a module and how often should I just write a function myself? Cool example though
Yeah, you’re absolutely right. It’s just something that I’ve written so often for different projects I figured I’d wrap it up as an npm package. There’s some level or DRYness to able to just import something like this rather than rolling the function for each project.
I like it a lot, this is something i had already thought of but never implemented, because as some comments say, i thought it might be a bit overkill. Anyway, i think there are cool use cases (specially for testing) where might be very useful. Used wisely :)
I do some machine learning engineering too and got my idea from grid search. I assume anyone using ML in JS could use this for parameter turning
Ok, that was kinda dumb :D I was working on the same solution, creating combination indexes by calculating all possible outcomes via rowLenght to the power of columnLenght and just after being so close I found this post :D
The only difference that I had is using different data structure:
Instead of:
Great tool! Thank you for sharing! 🙏
This is really cool 👍