bebraw
bebraw8mo ago

How to abstract tests to allow running different functions against the same test suite?

I've set up two suites of tests like this: https://github.com/gustwindjs/gustwind/tree/develop/htmlisp/html-tests, https://github.com/gustwindjs/gustwind/tree/develop/htmlisp/html-tests-sync . They are effectively the same but one is for async code and one for sync. Due to how the language works, technically it should be possible to use the async test suite for testing the sync code since await just wraps sync result within a Promise. The question is, what would be a sane way to extract the shared tests and then inject the functions so I could end up with a single test suite against both async and sync functions?
3 Replies
raunioroo
raunioroo8mo ago
I tend to wrap shared tests in a function, or functions which I then export (as functions I can run as test steps) . So the shared test file doesn't have side effects in the form of top level Deno.test() calls. Actual test suite then imports all the shared tests it wants and runs the functions. I find it good practice since this "libraries of test steps" approach has other bonuses: I can pass setup variables to the shared test step functions to simulate permutations of different environments or configs, or create fast mini test suites for things I'm currently working on, or have multiple levels of nested test steps - deno does handle and format the results of nested test steps quite nicely. I mean, for some reason I used to think test files are some special, limited dialect of JS. Once I realized it is just normal code which can be organized to modules, classes, functions, exports, imports like any other app, it suddenly felt like I gained testing superpowers.
bebraw
bebrawOP8mo ago
that sounds nice. do you have a public example somewhere? i think i'll start out by splitting up the tests anyway. i even thought about writing some type of JSON with input/output kind of format as that fits my use case and it's sort of tool agnostic
raunioroo
raunioroo8mo ago
Nothing public sry. But basically something along the lines of
// --------------- testsuite.js
import * as sharedTest_A from "./sharedtest_A.js";

Deno.test({
name: "testFoo",
fn: async (t) => {
await t.step("sharedTest_A", async (t) => {
await sharedTest_A.testFoo(t);
await sharedTest_A.testBar(t);
});
// await t.step("sharedtest_B" async (t) => { ... });
}
})

// --------------- sharedtest_A.js
export async function testFoo(t) {
await t.step("test 1", async (t) => {
// do something
});
await t.step("test 2", async (t) => {
// do something
});
}
export async function testBar(t) {
await t.step("test 3", async (t) => {
// do something
});
await t.step("test 4", async (t) => {
// do something
});

}
// --------------- testsuite.js
import * as sharedTest_A from "./sharedtest_A.js";

Deno.test({
name: "testFoo",
fn: async (t) => {
await t.step("sharedTest_A", async (t) => {
await sharedTest_A.testFoo(t);
await sharedTest_A.testBar(t);
});
// await t.step("sharedtest_B" async (t) => { ... });
}
})

// --------------- sharedtest_A.js
export async function testFoo(t) {
await t.step("test 1", async (t) => {
// do something
});
await t.step("test 2", async (t) => {
// do something
});
}
export async function testBar(t) {
await t.step("test 3", async (t) => {
// do something
});
await t.step("test 4", async (t) => {
// do something
});

}
Passing t around is needed for proper nesting of steps in the output.'