Okay so it seems, there is nothing really out there and I came up with my own cooked solution.
Generating tests dynamically as in Jest is in Mocha / Chai pretty simple and with the following helper function:
/**
*
* This is a helper function, that parses a markdown table via a tagged template literal
* and returns a function needing a callback to be passed; with which it is invoked
* for each row of the table with the values filled in with the columnHeaderName
*
* @param tableMarkup
* @param substitutions
*/
export function callForEachRow(tableMarkup: TemplateStringsArray, ...substitutions: any[]): (callback: Function) => void {
const headerMarkup = tableMarkup[0];
// filter all table headers by splitting with '|' on initial and removing everything empty after having stuff trimmed
const keys = headerMarkup.split('|')
.map(cellContent => cellContent.trim())
.filter(trimmedContent => trimmedContent !== '')
.filter(trimmedContent => trimmedContent !== '
')
.filter(trimmedContent => !trimmedContent.startsWith('-'));
// determine rowCount and offset index by rowCount
const rowCount = substitutions.length / keys.length;
const columnCount = keys.length;
return (callback) => {
// invoke callback for each row
const rows = Array.from({ length: rowCount });
let returnPromise = false;
// the result of each callback might either be a promise or not
// if it is promise, wait until it resolves
return rows.reduce((acc, current, row) => {
// create obj for current row
const obj = keys.reduce((acc, key, index) => {
// and shift through substitutions by rowOffset
acc[key] = substitutions[index + row * columnCount];
return acc;
}, {});
// determine, whether we need to return a promise...
const result = callback(obj, row);
if (row === 0 && result instanceof Promise) {
returnPromise = true;
acc = result;
}
// if we are to return a promise, we need to sequentially execute them
// by deferring their call to then handler of the previous one
if (returnPromise) {
// @ts-ignore because TS does not know, that we are dynamically changing the type
return acc.then(() => {
return result
});
} else {
return acc;
}
}, undefined);
};
}
I can either generate tests dynamically (as the test.each
) helper does:
callForEachRow`
| enableMixed | value | newValue |
| ------------ | ---------- | ---------- |
| ${false} | ${false} | ${false} |
| ${false} | ${false} | ${'mixed'} |
`(({ enableMixed, value, newValue }) => {
it(`should not be dispatched if the value changes from ${value} to ${newValue} with enableMixed set to ${enableMixed} `, async () => {
// ...
});
});
or simply generate expectations. The passed callback can either be async or sync. If it is async, each callback will be called after the previous resolved.
This allows to have descriptive data-driven tests, instead of manually writing a lot of test cases and possible forgetting some edge cases.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…