Skip to content

Instantly share code, notes, and snippets.

@cowboyd
Last active February 13, 2024 16:50
Show Gist options
  • Save cowboyd/70e4596e61aadafd852f403dc6d8ec6f to your computer and use it in GitHub Desktop.
Save cowboyd/70e4596e61aadafd852f403dc6d8ec6f to your computer and use it in GitHub Desktop.
A testing scope for Effection to help writing tests
import { run, type Operation } from "effection";
export interface TestScope {
/**
* Call from your runner's "beforeEach" or equivalent
*/
addSetup(op: () => Operation<void>): void;
/**
* Call from runner's `it()` or equivalent;
*/
runTest(op: () => Operation<void>): Promise<void>;
}
export function createTestScope(): TestScope {
let setup = [] as Array<() => Operation<void>>;
return {
addSetup(op) {
setup.push(op);
},
runTest(op) {
return run(function*() {
for (let step of setup) {
yield* step();
}
yield* op();
})
},
};
}
import * as bdd from "https://deno.land/[email protected]/testing/bdd.ts";
import { TestScope, createTestScope } from "./test-scope.ts";
import type { Operation } from "effection";
let scope: TestScope | void = void(0);
// Integrate test scope with the Deno test runner by wrapping the `describe()`, `it()` and `beforeEach()` methods.
function describeWithScope<T>(...args: bdd.DescribeArgs<T>): bdd.TestSuite<T> {
let [name, def] = args;
return bdd.describe(name as string, () => {
bdd.beforeEach(() => {
if (!scope) {
scope = createTestScope();
}
});
if (def && typeof def === "function") {
def();
}
});
}
describeWithScope.only = bdd.describe.only;
describeWithScope.ignore = bdd.describe.ignore;
export const describe: typeof bdd.describe = describeWithScope;
export function beforeEach(op: () => Operation<void>): void {
bdd.beforeEach(() => scope!.addSetup(op));
}
export function it(desc: string, op?: () => Operation<void>): void {
if (op) {
return bdd.it(desc, () => scope!.runTest(op));
} else {
return bdd.it.ignore(desc, () => {});
}
}
it.only = function only(desc: string, op?: () => Operation<void>): void {
if (op) {
return bdd.it.only(desc, () => scope!.runTest(op));
} else {
return bdd.it.ignore(desc, () => {});
}
};
import { createContext } from "effection";
import { describe, it, beforeEach } from "./02-deno.bdd.ts";
const context = createContext<string>("some-string");
describe("scenario", () => {
beforeEach(function*() {
yield* context.set("Hello World");
});
it("does something with context", function*() {
expect(yield* context).toEqual("Hello World");
})
})
@floratmin
Copy link

In effection 3 the beforeEach function should be:

beforeEach(function*(scope: Scope) {
  yield* scope.set(context, 'Hello World');
});

@cowboyd
Copy link
Author

cowboyd commented Jan 11, 2024

Thanks @floratmin! I've updated the gist.

@cowboyd
Copy link
Author

cowboyd commented Jan 11, 2024

@floratmin This got me to thinking: why not just run the beforeEach() inside the same scope as the test itself is running? This grossly simplifies things by just storing all the setup steps and running them right before the body of the test itself.

Among other things, there is no need to even have a destroy() at all because only ever one task is being executed, the actual test itself.

the only time that I could see keeping a scope around would be if you wanted to have a beforeAll() hook which this mechanism does not currently support.

@taras
Copy link

taras commented Feb 13, 2024

@cowboyd 02-deno-bdd.ts file is missing imports

-import { TestScope } from "./test-scope.ts";
+import { TestScope, createTestScope } from "./test-scope.ts";
+import type { Operation } from "effection";

@cowboyd
Copy link
Author

cowboyd commented Feb 13, 2024

@taras thanks! Updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment