How to unit test a script? #442
Replies: 3 comments 4 replies
-
Inspirationally, I suppose I'm hoping to write tests that could look like this: describe("example.mjs", () => {
it("calls `ls` with expected args", async () => {
const stub = jest.fn()
somehowRunScriptUnderTest("./example.mjs", { shell: stub })
expect(stub).toHaveBeenCalledWith("ls -lha")
})
it("formats the output", async () => {
const result = somehowRunScriptUnderTest("./example.mjs", {
shell: jest.fn(() => `
Permissions Size User Date Modified Name
drwxr-xr-x - user 16 Jun 21:59 .
drwxr-xr-x - user 7 Jun 17:30 ..
drwxr-xr-x - user 16 Jun 21:59 foo
drwxr-xr-x - user 27 May 14:53 bar
`),
})
expect(result.stdout).toEqual(`
Permissions Size User Date Modified Name
drwxr-xr-x - user 16 Jun 21:59 .
drwxr-xr-x - user 7 Jun 17:30 ..
drwxr-xr-x - user 16 Jun 21:59 foo
drwxr-xr-x - user 27 May 14:53 bar
`)
})
}) I'm no-doubt handwaving away important details in this pseudo-code, but just adding it here to explain my direction and intent with my question. I'm actually happy to pursue whatever approach zx supports and suggests. |
Beta Was this translation helpful? Give feedback.
-
@antonmedv, can you provide an example of how to do this? |
Beta Was this translation helpful? Give feedback.
-
Latecomer to the discussion, I had success with this half-baked mocking using vitest: import { describe, expect, test, vi } from 'vitest';
import type { ProcessPromise } from 'zx';
import { ProcessOutput } from 'zx';
// Needed for easy type-checking, very hackish
interface ProcessPromisePublic {
_stage: ProcessPromise['_stage'];
_reject: ProcessPromise['_reject'];
_resolve: ProcessPromise['_resolve'];
_from: ProcessPromise['_from'];
}
function resolveProcessPromise(
promise: ProcessPromise,
opts:
| {
stdout: string;
}
| Error,
) {
const promisePublic = promise as never as ProcessPromisePublic;
const output = new ProcessOutput({
code: opts instanceof Error ? 1 : 0,
signal: null,
error: null,
duration: 0,
store: {
stdout: 'stdout' in opts ? [opts.stdout] : [],
stderr: opts instanceof Error ? [opts.toString()] : [],
stdall: [],
},
from: promisePublic._from,
});
if (!output.ok && !promise.isNothrow()) {
promisePublic._stage = 'rejected';
promisePublic._reject(output);
} else {
promisePublic._stage = 'fulfilled';
promisePublic._resolve(output);
}
}
const mocks = vi.hoisted(function () {
const run = vi.fn(async function (this: ProcessPromise) {
switch (this.cmd) {
// Define here all possible commands
case 'my-command':
resolveProcessPromise(this, {
stdout: 'This is the stdout of my command',
});
break;
default:
resolveProcessPromise(
this,
// Log all non-implemented tests
new Error(
`ProcessPromise.run is not implemented in tests: ${this.cmd}`,
),
);
}
return this;
});
return {
run,
};
});
vi.mock(import('zx'), async (importOriginal) => {
const { ProcessPromise, ...rest } = await importOriginal();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(ProcessPromise.prototype.run as never as any) = mocks.run;
return {
...rest,
ProcessPromise,
};
});
// Then, later run tests as normal and use $`my-command` as usual.
describe('My test', () => {
test('Runs commands', async () => {
// Here you use $ as usual, and it will return the custom-defined stdout or error
})
}) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Let's say we have this super simple script that just calls
ls
:example.mjs
How may I test that this script actually calls
ls -lha
? How can I test the output is rendered correctly?In the real world I'm looking to test various calls and logic like
git ...
commands whose result are parsed and accumulated in various ways, but I think my question fundamentally boils down to how I can test anything at all. The bane of my existence has been effectively untestable Bash scripts, so I'm hoping to take a leap forward in maintainability with zx.Beta Was this translation helpful? Give feedback.
All reactions