I have some utils functions that I'm using among various Jest tests, for example a function like this, for mocking a fetch response:
export const mockFetchJsonResponse = (data) => {
ok: () => true,
json: () => data
};
I would like to share those functions in a way that I can import them and reuse among my tests. For example:
// Some .spec.jsx file
// ...
import {mockFetchJsonResponse} from 'some/path/to/shared/tests/utils.jsx'
// Then I can use mockFetchJsonResponse inside this test
// ...
Where should I place such common utils functions?
My project folder looks like this:
components/
CompOne/
__tests__
index.jsx
CompTwo/
__tests__
...
utils/
__tests__
http.js
user.js
...
Should I place them inside the utils folder together with other utils functions that I use for my project? Then should I write unit tests also for these functions?
There is an ability to expose helpers as global functions without any need to import modules explicitly.
Jest allows to configure some files will be run before every test file executed through setupFiles configuration option
Also Jest provides global object that you can modify and everything you put there will be available in your tests.
Example
package.json:
"jest": {
"setupFiles": ["helpers.js"]
}
helpers.js:
global.mockFetchJsonResponse = (data) => {
ok: () => true,
json: () => data
};
somecomponent.test.js:
mockFetchJsonResponse(); // look mom, I can call this like say expect()!
With TypeScript
TypeScript will complain with cannot find name 'mockFetchJsonResponse'. You can fix that by adding a declaration file:
helpers.d.ts:
declare function mockFetchJsonResponse(data: any): any;
Create a new tsconfig.test.json file and add that file to the files section and extend your main tsconfig:
{
"extends": "./tsconfig.json",
"files": ["./.jest/helpers.d.ts"]
}
In your jest.config.js file, add a new global setup for ts-jest to have jest use your new tsconfig file:
// ...
globals: {
"ts-jest": {
tsconfig: "tsconfig.test.json"
}
}
// ...
Sure it does not answer you direct question "where to put the files" but it's anyway up to you. You just need specify those files in setupFiles section. Since there is no import needed in tests it does not really matter.
As for testing test helpers I'm not sure. See it's part of testing infrastructure like spec file itself. And we don't write tests for tests or it would never stop. Sure, it's up to you - say if logic behind is really-really complex and hard to follow. But if helper provides too complex/complicated logic it would lead to tests themselves be impossible to understand, do you agree?
kudos to that article on testing compoentns with intl. Have never dealt with globals in jest before.
TL;DR; create a /__utils__/ and update testPathIgnorePatterns
Full answer:
Here's just a suggestion:
testPathIgnorePatterns: ['/__fixtures__/', '/__utils__/'],
I use /__tests__/ for the tests and within it sometimes I need to add a folder with data that will be used by those tests, so I use /__fixtures__/ folder.
Likewise, when I have a shared logic across tests, I place them at /__utils__/ folder (also within /__tests__/)
For more details, please read more about testPathIgnorePatterns
Another approach is by having a test directory and moving helpers on it.
src/
components/
utils/
...
test/
testHelpers.js
Then on the test:
// src/components/MyComponent.spec.js
import { helperFn } from '../../test/testHelpers';
Benefits:
Be explicit of where the function is coming from
Separate helpers that need to be tested from those that do not ¹
Drawbacks:
The test directory might look silly by containing just a helper file
AFAIK this approach is no where specified on official documentation
Looks like GitLab is implementing this approach on their RoR project.
¹ no matter which approach you take, please don't test the test helpers. If the helper fails then your test must fail too. Otherwise your helper is not helping at all.
Related
I want to convert my mock implementation for path (specifically "join") in to a __mocks__ folder in a file called path.js
Currently I have this in my io.test.js file and it works:
vi.mock("path", () => {
return {
default: {
join: (...args) => {
return args[args.length - 1];
},
},
};
});
How would I do this in the __mocks__\path.js file instead?
Vitest would normally look for the mock file with the same name as the mocked one in the __mocks__ folder under the project root folder. However I've found this a bit problematic, as on one hand having an additional root folder like that, instead for example one under test folder may not be what everyone wants (ugly?), but more importantly sometimes the name of the original import is not trivial to guess, e.g. for modules from node_modules, most the time the import path may not really be a filename, so what the file under __mocks__ should be called can become tedious guess-game...
Instead one can use this syntax to use a file based mock from any location:
// The test file
vi.mock('path', async () =>
await vi.importActual('another/path/to/the/mock.js')
)
// another/path/to/the/mock.js
export default {
join: (...args) => {
return args[args.length - 1];
},
}
Having it set up like this you can put the mock file anywhere you want.
Note that mocks under the root __mocks__ would however be automatically picked up by Vitest, so using the same folder for these kind of manual mocks can lead to confusion. I'd recommend puting them under another folder. For me test/vitest/mocks or similar seems more logical.
I have created a number of String.prototype functions which for maintainability I'd like to have in its own file. That is, I'd like to include the file in a javascript project and thus have all the String functions defined.
I could create a module that exports each function, but then I'd have to assign each function as its own String prototype, yes? Something like
var myStringFunctions = require("myStringFunctions");
String.prototype.func1 = myStringFunctions.func1;
Is there a way to include such a file so that the prototypes are defined as part of the inclusion?
Try it, you will see your code and using require("./myStringFunctions"); works just fine.
./myStringFunctions.js
String.prototype.func1 = function() {
return this.toUpperCase(this);
};
./index.js
require("./myStringFunctions");
console.log("foo".func1()); // FOO
If your JS is going to run in the browser, you can use JS modules with the import and export syntax if you use a module bundling build tool like Webpack: https://webpack.js.org/ .
If your JS is running in a Node.js environment, modules are supported: https://www.w3schools.com/nodejs/nodejs_modules.asp
With this code
const noCurrencies: Map<CurrencyCode, Currency> = new Map();
/**
* Reducer for the finished currency request.
*/
export function currencies(state: Map<CurrencyCode, Currency> = noCurrencies, action: Action): Map<CurrencyCode, Currency> {
switch (action.type) {
case SET_CURRENCIES:
return action.currencies;
default:
console.log('currencies(reducer): noCurrencies', noCurrencies)
return state;
}
}
I get this console output:
currencies(reducer): noCurrencies undefined
Is this a known problem with Babel? How do I debug it? I have a feeling that it's due to this particular file having been called twice during initialisation and thus having a circular dependency with another file.
(I'm not 'recreating a repro from scratch', so don't suggest that, and the types are https://github.com/flowtype/flow-typed which get removed in a pre-processor step, and I've tried without types as well, with the same result)
The reason was a large circular dependency between modules. Even if using ECMAScript module syntax, the compiler (WebPack 3 + babel) is not smart enough to only load what is needed when it's needed; but instead evaluate all exports and imports whenever a file is used/touched.
That means that if you have index.js files that liberally export things from around them, to create a single entry-point for callers outside its folder, you'll likely run into issues like this.
I had to go through the code-base and make the imports 'deep' by directing imports straight to the, of the index file, surrounding files, instead.
E.g.
folder A
epics.js
commands.js
index.js
types.js
actions.js
reducers.js
messages.js
...
folder B
import { create } from '../A' becomes import { create } from '../A/types'
and so forth for all things; in the end I'm only exporting React views and types from index.js
I am trying to put together a module based architecture for my Meteor/Node application.
I have a client/main.js importing a imports/module1/index.js.
The imports/module1/index.js imports a imports/module1/api/api.js after it imports a component imports/module1/component/component1.js.
To sum it up, the simplified tree would look like this
.client/main.js
|_imports/module1/index.js
|_imports/module1/api/api.js
|_imports/module1/component/component1.js
The api.js file looks like that:
export default {
myFunction1 (arg1, arg2) {
// function stuff
},
myFunction2 (arg1, arg2) {
// function stuff
},
}
I expected to be able to call myFunction1(ar1,arg2) inside the imports/module1/component/component1.js but it doesn't work. What am I missing?
Currently you don't import api.js in your component1.js file so component1 has no access to any functions defined in the api.
Scope of a file doesn't "leak" so each file is completely separate in this matter. import in the main.js file doesn't automatically make all the imported modules available to its child modules – this wouldn't make sense. You have to import something in order to be able to use it.
I tried to use gulp, browserify, watchify, and babelify to compile a standalone js module.
The options is like this:
const browserifyOpts = {
debug: true,
entries: path.join('src', 'index.js'),
standalone: 'Goo',
transform: [
'babelify',
],
};
My JS module is as follows:
export default class Goo {
constructor() {
this.a = 'hello';
}
}
It compiles fine. But when I include it in a script tag in a html file, the Goo module is wrapped inside window.Goo.default, that is, I cannot directly use:
new Goo();
but have to use:
new Goo.default();
Any idea where went wrong?
Thanks!
The easiest option would be to wrap this yourself by having your index.js file contain something like
module.exports = require('./main').default;
and move your current index.js to main.js. Then you'll be set.
As the other answer says, you can potentially use babel-plugin-add-module-exports, but that is not something I'd generally recommend because while it fixes this problem, it also introduces the possibility that you could accidentally write import/export pairs that work but are not spec-compliant.
By default, Babel 6 handles default module exports differently. If you want to use the legacy version, you can use the add-module-exports plugin.
Either add that into your .babelrc, or add it as an option in Babelify.
More information from this Github Issue:
has to do with the new way Babel handles default exports. if you dont
want to have to reference the default key, you need to use this
plugin: https://github.com/59naga/babel-plugin-add-module-exports