Skip to content

Instantly share code, notes, and snippets.

@Zodiase
Created January 24, 2025 00:48
Show Gist options
  • Save Zodiase/f357b14cc8f4d45c1da4c1f89603f211 to your computer and use it in GitHub Desktop.
Save Zodiase/f357b14cc8f4d45c1da4c1f89603f211 to your computer and use it in GitHub Desktop.
Make side effects easier to clean in tests.
// Use case
describe('some tests', () => {
const setEnvVar = useSideEffect((dynamicVal1: string, dynamicVal2: number) => {
process.env.SOME_ENV_VAR_1 = dynamicVal1;
process.env.SOME_ENV_VAR_2 = String(dynamicVal2);
return () => {
// Keep cleanup code close to the side-effect code.
delete process.env.SOME_ENV_VAR_1;
delete process.env.SOME_ENV_VAR_2;
};
});
afterAll(() => {
// A single place to clean up all side effects from `setEnvVar`.
setEnvVar.cleanup();
});
it('some test', () => {
setEnvVar.apply('some-value', 123);
});
});
export interface SideEffect<T extends unknown[]> {
/**
* Applies the side effect.
* @param args Arguments to pass to the side effect function.
*/
apply(...args: T): void;
/**
* Cleans up the side effect from all previous apply calls.
*/
cleanup(): void;
}
type CleanUpFn = (...args: unknown[]) => unknown;
/**
* This is helper function to create a side effect that can be easily applied and cleaned up.
* @param fn The function that applies the side effect when `apply` is called. It's expected to return a function to clean up for that call.
* @returns An object with two methods: `apply` and `cleanup`. `apply` is used to apply the side effect, and `cleanup` is used to clean up the side effect.
* Arguments passed to `apply` are passed to `fn`.
* Apply can be called multiple times, and the cleanup function will be called for each apply call.
*/
export function useSideEffect<T extends unknown[]>(fn: (...args: T) => CleanUpFn): SideEffect<T> {
const cleanupFns: Array<CleanUpFn> = [];
return {
apply: (...args: T) => {
const cleanupFn = fn(...args);
// Later applies are cleaned up first.
cleanupFns.unshift(cleanupFn);
},
cleanup: () => {
cleanupFns.forEach(cleanupFn => {
cleanupFn();
});
},
};
}
@Zodiase
Copy link
Author

Zodiase commented Jan 24, 2025

This is a rough v1 that doesn't consider async functions.

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