I have encountered issues while writing unit tests, and I am currently facing errors that I am trying to troubleshoot.
The specific unit test concerns the index.ts
file, which calls the features/index.ts
file. To simulate the default export from features/index.ts
, I am using sinon for stubbing. However, running the tests results in an error message indicating
TypeError: Cannot read property 'resolve' of undefined
, with the source being pointed to the features/feature1.ts
file.
Below are relevant snippets extracted from the testing setup and TypeScript files:
features/feature1.ts
import path from "path";
import fs from "fs";
import {Setup} from "../types";
const TEMPLATE_ROOT = path.resolve(__dirname,"../../templates");
const INDEX_TEMPLATE = fs.readFileSync(TEMPLATE_ROOT, "index.js", "utf8");
export const setup: Setup = async ({config, options}) => {
// Internal code removed
}
features/index.ts
import {setup as feature1} from "./feature1.ts";
import {setup as feature2} from "./feature2.ts";
type FeatureTypes = "feature1" | "feature2"
type Features = {
[key in FeatureTypes]: Setup;
};
const features: Features = {
feature1: feature1,
feature2: feature2
}
export default features
index.ts
import features from "./features"
import { Config, Options } from "./types";
export async function init(config: Config, options: Options): Promise<void> {
const nextFeature = options.features ? options.features.shift() : undefined;
if (nextFeature) {
// Other irrelevant code
await Promise.resolve(features[nextFeature]({ config, options }));
return init(config, options);
}
}
index.spec.ts
import { expect } from "chai";
import * as sinon from "sinon";
import { init } from '.';
import * as features from "./features";
import { Config, Options } from "./types";
describe("init", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let featuresStub: sinon.SinonStub;
beforeEach(() => {
featuresStub = sandbox.stub(features, "default").returns({
feature1: sandbox.stub().resolves(),
feature2: sandbox.stub().resolves(),
});
});
afterEach(() => {
sandbox.restore();
});
it("should call setup features", async () => {
const setup: Setup = {
features: [
"feature1",
"feature2",
],
};
await init({}, options);
expect(featuresStub).to.have.been.calledOnce;
});
// rest of tests
});
I have also attempted changing the stub setup to:
import * as feature1 from ".features/feature1";
import * as feature2 from ".features/feature2";
// Other code
describe("init", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let feature1Stub: sinon.SinonStub;
let feature2Stub: sinon.SinonStub;
beforeEach(() => {
feature1Stub = sandbox.stub(feature1, "setup");
feature2Stub = sandbox.stub(feature2, "setup");
feature1Stub.resolves()
feature2Stub.resolves()
});
// Rest of code and tests
});
I am puzzled as to why it's attempting to execute the code
const TEMPLATE_ROOT = path.resolve(__dirname,"../../templates");
even though I have stubbed the function calling it.