Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jonathanconway/combinator
Generates lots of combinations – useful for unit tests.
https://github.com/jonathanconway/combinator
Last synced: about 13 hours ago
JSON representation
Generates lots of combinations – useful for unit tests.
- Host: GitHub
- URL: https://github.com/jonathanconway/combinator
- Owner: jonathanconway
- Created: 2022-10-10T03:16:01.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-10-10T07:10:27.000Z (over 2 years ago)
- Last Synced: 2024-12-12T21:38:28.680Z (29 days ago)
- Language: TypeScript
- Size: 98.6 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Combinator
> Generates combinations of things – useful for unit tests.
## Installation
```bash
npm install combinator-util
```## Usage
```js
import { combinate } from "combinator-util";test("example", () => {
const combinations = combinate({
color: ["r", "g"],
brightness: [100, 200],
});expect(combinations).toEqual([
{
brightness: 100,
color: "r",
},
{
brightness: 200,
color: "r",
},
{
brightness: 100,
color: "g",
},
{
brightness: 200,
color: "g",
},
]);
});
```## Background
### _Problem_
Sometimes we want to unit test a function very thoroughly, ensuring that it returns a correct result for a very large number of combinations of arguments.
If we try to hard-code every combination into our unit test, we will run into two problems:
1. It will be laborious to enter all the combinations manually (or we will have to use some external tool to generate the combinations and then copy & paste them into our code)
2. The test code will be very long and verbose - difficult to read and maintain### _Solution_
Combinator aims to solve these problems by providing functionality to generate combinations of values in an organised and maintainable manner.
## Example
For historical reasons, determining the number of days in a month in the Western calendar is complicated.
The following short rhyme tries to summarise the rules in a memorable way:
> Thirty days have September,
> April, June and November.
> All the rest have thirty-one,
> except February alone, which has
> twenty-eight days each year
> and twenty-nine days each leap-year
Suppose we wanted to unit-test a function, `getDaysInMonth`, which takes `month` and `year` as input and returns a number of `days`.
We could simply input every possible date into the unit test and assert on the month of each. As mentioned above, that could involve quite a lot of fiddling in Excel and would result in a very long and not very human-readable test file.
Let's see how we would tackle this problem in Combinator.
Starting with the first two lines of the rhyme:
> Thirty days have September,
> April, June and November.
We can express it programatically like this:
```ts
const thirtyDays = combinate({
year: range(2020, 2023), // non-leap-years
month: ["april", "june", "september", "november"],
expectedDays: [30],
});// output: [{ year: 2020, month: 'april', expectedDays: 30 }, ...]
```The result can easily be passed into a data-driven test in Jest:
```ts
it.each(thirtyDays)(
"$month in $year should have $expectedDays days",
({ month, year, expectedDays }) => {
expect(getDaysInMonth(month, year)).toBe(expectedDays);
}
);
```On running the unit test, the following test cases will be generated and executed:
```ts
✓ april in 2020 should have 30 days (3 ms)
✓ june in 2020 should have 30 days
✓ september in 2020 should have 30 days
✓ november in 2020 should have 30 days
✓ april in 2021 should have 30 days
✓ june in 2021 should have 30 days (1 ms)
✓ september in 2021 should have 30 days
✓ november in 2021 should have 30 days (1 ms)
✓ april in 2022 should have 30 days
✓ june in 2022 should have 30 days
✓ september in 2022 should have 30 days
✓ november in 2022 should have 30 days
✓ april in 2023 should have 30 days
✓ june in 2023 should have 30 days
✓ september in 2023 should have 30 days
✓ november in 2023 should have 30 days
```Notice how we can use a small amount of code (in this example, 5 lines for the `combinate` call) to generate a much larger set of test cases (16). This gives our test code more leverage.
Covering the remaining lines of the rhyme:
> All the rest have thirty-one,
```ts
const thirtyOneDays = combinate({
year: range(2020, 2023),
month: ["january", "march", "may", "july", "august", "october", "december"],
expectedDays: [31],
});
```> except February alone, which has
> twenty-eight days each year
```ts
const februaryDays = combinate({
year: [2023],
month: ["february"],
expectedDays: [28],
});
```> and twenty-nine days each leap-year
```ts
const februaryLeapYearDays = combinate({
year: [2024],
month: ["february"],
expectedDays: [29],
});
```Notice how we can assign meaningful names to each of the test case variables, increasing the readability of the test code.
## API
### `combinate` (function)
Generate multiple combinations of T, based on a definition of what combinations of T should be generated.
**Parameters:**
- definition (`Definition`) - Object which, for each property of T, provides an array of possible values of that property.
**returns:** T[]
```tsx
// Returns:
// [
// { color: 'r', brightness: 100 },
// { color: 'r', brightness: 200 },
// { color: 'r', brightness: 300 },
// { color: 'g', brightness: 100 },
// { color: 'g', brightness: 200 },
// { color: 'g', brightness: 300 },
// { color: 'b', brightness: 100 },
// { color: 'b', brightness: 200 },
// { color: 'b', brightness: 300 }
// ]
combinate({
color: ["r", "g", "b"],
brightness: [100, 200, 300],
});
```### `Definition` (type)
Definition of combinations of properties of T.
Each property is a property of T, and each value is an array of values of that property.### `range` (function)
Utility which generates the set of numbers between `from` and `to`.
**Parameters:**
- from (`number`) - Start of the range. E.g. `2`
- to (`number`) - End of the range. E.g. `4`**returns:** number[]
```tsx
// Returns [2, 3, 4]
range(2, 4);
```## Contributing
To get set up developing this library simple install the dependencies via NPM.
```bash
npm install
```To build the project as an importable library, run build.
```bash
npm run build
```If you make any changes to the API, please maintain the API docs and re-build the docs before merging the changes.
```bash
npm run generate-docs
```## Contributors ✨
Jonathan Conway ([conwy.co](http://conwy.co))
Contributions of any kind are welcome!