I'm using the proxyquire library, which mocks packages on import.
I'm creating my own proxyquire function, which stubs a variety of packages I use regularly and want to stub regularly (meteor packages, which have a special import syntax):
// myProxyquire.js
import proxyquire from 'proxyquire';
const importsToStub = {
'meteor/meteor': { Meteor: { defer: () => {} } },
};
const myProxyquire = filePath => proxyquire(filePath, importsToStub);
export default myProxyquire;
Now I want to write a test of a file which uses one of these packages:
// src/foo.js
import { Meteor } from 'meteor/meteor'; // This import should be stubbed
export const foo = () => {
Meteor.defer(() => console.log('hi')); // This call should be stubbed
return 'bar';
};
And finally I test it like this:
// src/foo.test.js
import myProxyquire from '../myProxyquire';
// This should be looking in the `src` folder
const { foo } = myProxyquire('./foo'); // error: ENOENT: no such file
describe('foo', () => {
it("should return 'bar'", () => {
expect(foo()).to.equal('bar');
});
});
Note that my last 2 files are nested inside a subfolder src. So when I try to run this test, I get an error saying that the module ./foo couldn't be found, as it is being looked for in the "root" directory, where the myProxyquire.js file is, not the src directory as expected.
You might be able to work around that (expected) behaviour by using a module like caller-path to determine from which file myProxyquire was called, and resolving the passed path relative to that file:
'use strict'; // this line is important and should not be removed
const callerPath = require('caller-path');
const { dirname, resolve } = require('path');
module.exports.default = path => require(resolve(dirname(callerPath()), path));
However, I have no idea of this works with import (and, presumably, transpilers).
Related
I'm trying to use mock-fs to unit test code which uses ES6 dynamic imports.
There seems to be an unexpected coupling between tests when I'm using dynamic imports, even though I call restore() after each test. It appears as though fs.readFile() behaves as expected between tests (no coupling), but await import() has coupling (it returns the result from the previous test).
I've created a minimal Jest test case that reproduces the issue. The tests pass individually, but not when run together. I notice that if I change the directory value so it's different between each test, then they pass together.
Can you help me understand why this doesn't work, whether it's a bug, and what I should do here?
import path from 'path';
import { promises as fs } from 'fs';
import mockFs from 'mock-fs';
const fsMockModules = {
node_modules: mockFs.load(path.resolve(__dirname, '../node_modules')),
};
describe('Reproduce dynamic import coupling between tests', () => {
afterEach(() => {
mockFs.restore();
});
it('first test', async () => {
const directory = 'some/path';
mockFs({
...fsMockModules,
[directory]: {
'index.js': ``,
},
});
await import(path.resolve(`${directory}/index.js`));
//not testing anything here, just illustrating the coupling for next test
});
it('second tests works in isolation but not together with first test', async () => {
const directory = 'some/path';
mockFs({
...fsMockModules,
[directory]: {
'index.js': `export {default as migrator} from './migrator.js';`,
'migrator.js':
'export default (payload) => ({...payload, xyz: 123});',
},
});
const indexFile = await fs.readFile(`${directory}/index.js`, 'utf-8');
expect(indexFile.includes('export {default as migrator}')).toBe(true);
const migrations = await import(path.resolve(`${directory}/index.js`));
expect(typeof migrations.migrator).toBe('function');
});
});
I'm can't understand some things here related to electron. I've been searching for hours for the magic answer but couldn't find anything.
My goal is simple. I don't want my main electron.js file to be 5000 lines long without any kind of organization so I'm trying to split the code into multiple js file that make sense.
My idea was to use import { someFunction1, someFunction2 } from './scripts/someScript' in my electron.js and then just creating that file with the arrow function in it and export them.
Then I could call the function as I want in the main file. However, it doesn't seem to be possible. I've read electronjs doesn't support ES6 syntax. I've read about Babel (But from what I read, it implies a bunch of additional configuration and I don't want to spend days trying to add this to the bunch of configuration that are already messy with electron + React (No boiler plate here). And I didn't find anything specifics for this combo.
Question is. Is this doable in 2021? Am I missing anything? What would you guys recommend?
File would look something like this:
import { someNodeModuleFunction } from 'someNodeModule';
const someFunction1 = () => {
return 1;
};
const someFunction2 = () => {
return 2;
};
export { someFunction1, someFunction2 }
EDIT
Here's the actual code I have a problem with. I still get
if the file is .js: "Must use import to load ES Module"
If the file is .mjs: "Cannot use import statement outside a module"
This script is simply using fs to create a directory:
DataManagement.mjs:
import { existsSync, mkdir } from 'fs';
const electron = require('electron');
const app = electron.app;
const documentFolder = app.getPath('documents');
const CreateDataDirectory = () => {
const mainPath = documentFolder + 'AppName'
if (!existsSync(mainPath)) {
mkdir(mainPath);
}
};
module.exports = { CreateDataDirectory }
Calling it like that in electron.js:
const { CreateDataDirectory } = require('./scripts/DataManagement.mjs');
[...]
CreateDataDirectory()
Not sure how dividing the code can be that hard. :)
You may need to use Node.js module syntax (require and module.exports) or Babel (code transpiler).
For example:
import { someNodeModuleFunction } from 'someNodeModule';
const someFunction1 = () => {
return 1;
};
const someFunction2 = () => {
return 2;
};
module.exports = { someFunction1, someFunction2 }
Using your module:
const { someFunction1, someFunction2 } = require ('./FILE.js');
// ...
You could use module.exports:
otherModule.js
const someFunction1 = () => {
return 1;
};
const someFunction2 = () => {
return 2;
};
module.exports = {
someFunction1,
someFunction2
};
main.js
const { someFunction1, someFunction2 } = require('otherModule.js');
I want to write unit tests for myFunction() in the server.js file:
Main file: server.js
const config = require('./configuration'); // <==== local js file that breaks when unit testing
const other = require('some-module'); // <==== some module that is needed and it does not break when testing.
module.exports { myFunction };
function myFunction() {
// ...
}
function someOtherFunctions() { /*...*/ }
Test file: server.test.js:
const server = require('../server'); // <=== one level up in the folder hierarchy
it('should work', () => {
});
Problem:
jest breaks at const config = require('./configuration') and I don't actually need this to test myFunction.
However, I need the second: require('some-module').
I need to mock or bypass the first const config = require('./configuration').
Question:
How can I only import the myFunction() from server.js and somehow mock the first require(...) statements in the server.js ?
I am trying to write unit tests of a class which utilizes sqlite3, using TypeScript.
The class itself can correctly utilize sqlite3, but when I try to test the functions which use sqlite3, I get the following error message:
Test suite failed to run
package.json does not exist at C:\...\node_modules\sqlite3\package.json
When I click that link, however, it opens up the package.json file. So it does, actually, exist.
A simplified version of the class under test is as follows:
import sqlite3 from "sqlite3";
import path from "path";
import fs from "fs";
export class DataManager {
database_path: string;
gamertags: { [key: string]: any };
api: object;
constructor() {
//Prepare database
this.database_path = path.join(__dirname, "..", "data", "DBName.db");
}
public mainloop = async () => {
//Setup database
this.setup_db(this.database_path);
};
setup_db = async (db_path: string) => {
//Create data directory if it doesn't exist
if (!fs.existsSync(path.join(__dirname, "..", "data"))) {
fs.mkdirSync(path.join(__dirname, "..", "data"));
}
//Get read/write connection to database
const db = new sqlite3.Database(db_path);
//Serialize context to make sure tables are created in order
db.serialize(() => {
//Create table_1
db.run(`sql_query...`);
//Create table_2
db.run(`sql_query...`);
});
};
}
And my unit tests:
import { DataManager } from "../../core/dataManager";
import { mocked } from "ts-jest/utils";
import fs from "fs";
import sqlite3 from "sqlite3";
jest.mock("fs");
jest.mock("sqlite3");
let manager: DataManager;
const fake_database_path: string = "fake/database/path";
describe("datamanager.setup_db", () => {
beforeEach(() => {
jest.resetAllMocks();
manager = new DataManager();
manager.database_path = fake_database_path;
});
it("should create a new database file if one does not exist", async () => {
//Arrange
mocked(fs).existsSync = jest.fn().mockReturnValue = false as any;
mocked(sqlite3).Database = jest.fn() as any;
//Act
await manager.setup_db(fake_database_path);
});
});
I'm just not sure what on earth is preventing sqlite3 from finding its own package.json. None of my other dependencies seem to cause a fuss, yet sqlite3 does.
I'm trying to use proxyquire to replace a private function for testing in my Meteor app.
Meteor 1.6.1
meteortesting:mocha#1.1.2
In my parentFunction.js:
import { some function } from 'anotherFile';
function childFunction() {
...
return someValue;
}
export default function parentFunction() {
return childFunction()
}
In my test file:
const proxyquire = require('proxyquire');
if (Meteor.isServer) {
...
describe('parentFunction', () => {
it('uses the mocked child function', () => {
const testThing = proxyquire('./parentFunction', {
'childFunction': () => ({ 'name': 'bob' }),
});
});
}
parentFunction.js is in the same folder as my test file, and just to double check the path, I made sure this works:
import parentFunction from './parentFunction';
But when I run the test, I'm seeing an error:
Error: Cannot find module './parentFunction.js'
What am I doing wrong? I've tried an absolute path, that didn't work. And as far as I can see from the documentation a relative path in the file where proxiquire is required, should be fine.
Thanks for any help!