Skip to content

Instantly share code, notes, and snippets.

@cowboyd
Last active February 13, 2024 16:50
Show Gist options
  • Select an option

  • Save cowboyd/70e4596e61aadafd852f403dc6d8ec6f to your computer and use it in GitHub Desktop.

Select an option

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/std@0.163.0/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
Copy Markdown

In effection 3 the beforeEach function should be:

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

@cowboyd

cowboyd commented Jan 11, 2024

Copy link
Copy Markdown
Author

Thanks @floratmin! I've updated the gist.

@cowboyd

cowboyd commented Jan 11, 2024

Copy link
Copy Markdown
Author

@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

taras commented Feb 13, 2024

Copy link
Copy Markdown

@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

cowboyd commented Feb 13, 2024

Copy link
Copy Markdown
Author

@taras thanks! Updated.

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