Overview
I have a simple module written in nodejs that uses fs-extra package to test if a file exists. The module throws when the path exists and proceed to next procedure otherwise. Here is the source file:
// - main.js -
import fs from 'fs-extra'
export default async (pathName) => {
// Do not proceed if path already exists.
if (await fs.pathExists(projectPath)) {
throw new Error(chalk.red.bold(`${projectPath} already exists`))
}
// more logic here
}
I want to write a unit test that tests the bellow logic:
If filepath exists, we expect to throw an error
I don't want to mess up with the real filesystem -in case my code contains some nasty bug that could destroy it- so I went to an alternative solution, mocking the filesystem using mock-fs. Here is the spec file:
// - main.js spec file -
import mainFunction from '../main'
import mockfs from 'mock-fs'
describe('test main function', () => {
beforeEach(() => {
mockfs({
home: {
user: {
dummy: {}
}
}
})
})
test('expect to throw', async () => {
await mainFunction('/home/user/dummy')
})
afterEach(() => {
mockfs.restore()
})
})
What's the problem?
Every time I run the test, the main function does not throw. This happens because mockfs fake-filesystem was declared in the spec file, so the fs module in main source file does not know for the mockfs fake-filesystem and checks the real one. By the time that I do not have a folder named /home/user/dummy in my real filesystem the check always fails.
Expected behaviour
mainFunction in spec file should throw
Actual behaviour
mainFunction in spec file DOES NOT throw
Other info
I guess that I can turn this unit test into an integration test. But I do not want to. Is there any fix for this? Do I have to use another packages?
My test suit is Jest 22.3.0.
After some search, I found the appropriate way to unit test the branch. We really do not have to use the mock-fs module. We just have to mock pathExists method of fs-extra module to return one time the value false and one time the value true. Bellow, I post a working version of my spec file:
import mainFunction from '../main'
require('fs-extra').pathExists = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)
describe('test main function', () => {
beforeEach(() => {
jest.clearAllMocks()
})
test('expect to not throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).resolves
})
test('expect to throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).rejects.toBeInstanceOf(Error)
})
})
Related
I'm trying to use Jest in conjunction with ts-jest to write unit tests for a nodeJS server. I have something set up very similar to below:
impl.ts
export const dependency = () => {}
index.ts
import { dependency } from './impl.ts';
export { dependency };
consumer.ts
import { dependency } from '../impl' <- importing from index.ts
export const consumer = () => {
try {
dependecy();
return true;
} catch (error) {
return false;
}
}
consumer.test.ts
import * as dependencies from '../impl'
import { consumer } from './consumer'
const mockDependency = jest.spyOn(dependencies, 'depenedncy');
describe('function consumer', function () {
beforeEach(function () {
mockDependency.mockReturnValueOnce(false);
});
test('should return true', () => {});
})
This is just toy code, but the actual export / import / test files follow a similar structure. I'm getting typescript errors along these lines:
TS2769: No overload matches this call.
Specifically, that the method being spied on is not part of the overload of the import for dependencies, so I can't stub it out. I am doing literally the same thing in a different test file and it has no issues. Anyone know how to resolve the typing issue?
The issue turned out to be in the typing of the dependency function itself. The return value typing was incorrect and that was what was resulting in the Typescript error. Essentially I had this:
export const dependency: Handler = () => {
return () => {} <- is of type Handler
}
rather than this
export const dependency = (): Handler => {
return () => {} <- is of type Handler
}
Stupid mistake, hope it helps someone else in the future. My take away is that if you have a type error that doesn't make sense make sure you check the typing of all variables involved.
My code is expected to be run as
node index.js conf/config1.json conf/config2.json
I am writing tests to validate
conf/config1.json` and `conf/config2.json
I was hoping to do something like
npm test -- conf/config1.json conf/config2.json
Where in package.json, scripts.test points to jest.
This does not work; it fails with this error:
testRegex: - 0 matches
In other words, with the above command, jest looks for test files in the conf folder.
test/args.test.js:
const { describe, test, expect, toBe } = require('#jest/globals')
describe('Configuration checks', () => {
describe('CLI arguments check', () => {
test('CLI arguments are exactly two in number', () => {
// Write expect code here
})
test('Configuration file parameter is an existing file', () => {
// Write expect code here
})
test('Configuration file parameter allows reading', () => {
// Write expect code here
})
})
})
i try to use the mocha utils stackTraceFilter() function
but i cannot find an example usage case where someone explains how to use it in ones test. I found the official tests here: link
But how can i implement it in my tests, which somehow look like that:
import { expect } from 'chai'
import 'mocha'
import { main, main2 } from './'
describe.only('index.ts', async () => {
it('should start a job', async () => {
// const R_RUN_MAIN = await main()
await main2()
// TEST
expect(1).to.equal(1) // fails
})
})
In the tests i can see the line
expect(filter(stack.join('\n')), 'to be', stack.slice(0, 3).join('\n'));
But how do i get the Stack for my test?
expect(1).to.equal(1) // fails
or in general, how do i get the stack and initialize the filter function for the whole file when, for example, code from an imported file is already failing and creating a long stack trace?
UPDATE (2018.08.15)
so i got mocha running in a programmatic way:
export {}
import * as MOCHA from 'mocha'
async function run() {
const mocha = new MOCHA({
reporter: 'progress',
reporterOptions: {
verbose: true,
},
})
mocha.addFile(`./src/utils/mocha/index.spec.ts`)
const R = mocha.run((failures) => {
process.on('exit', () => {
process.exit(failures)
})
})
}
run()
I dont know where to add and run the Filter function?
const filter = MOCHA.utils.stackTraceFilter
The stackTraceFilter() function in mocha isn't meant to filter your code, but rather the mocha internals that in theory shouldn't be relevant to your tests. You can view the source code, but to sum it up it just filters out 'mocha' and 'node' lines from the stack, depending on the environment you're in.
I think what you're looking for could be accomplished through the package StackTraceJS, which allows you to grab a stack from anywhere, and do what you want with it. We created a custom reporter for mocha which uses it, and it works quite well.
So, using the example from their site:
StackTrace.get()
.then(function(stack){
// you now have a stack, and can filter as you wish
})
.catch(function(err){});
I have a Node.js project that I'm testing using Jest. I have several test files that have the same setup requirement. Previously, all these tests were in one file, so I just had a beforeAll(...) that performed the common setup. Now, with the tests split into multiple files, it seems like I have to copy/paste that beforeAll(...) code into each of the files. That seems inelegant - is there a better way to do this, ideally where I can just write my beforeAll(...)/setup logic once, and "require" it from multiple test files? Note that there are other tests in my test suite that don't require this setup functionality, so I don't want to make all my tests run this setup (just a particular subset of test files).
If you're using Jest >=20, you might want to look into creating a custom jest-environment for the tests that require this common setup. This would be a module that extends either jest-environment-node or jest-environment-jsdom, and implements async setup(), async teardown(), and async runScript() to do this setup work.
You can then add a #jest-environment my-custom-env directive to those files that require this setup.
See the Jest config docs for testEnvironment for details on how to set this up; there's a simple example there.
I am using a simple "test hooks" pattern for this:
// This function wraps beforeAll and afterAll into a single RAII-like call.
// That makes the describe code further down easier to read and makes
// sure you don't forget the afterAll part. Can easily be shared between tests.
function useFakeServer() {
let server;
beforeAll(() => server = sinon.fakeServer.create());
afterAll(() => server.restore());
return () => server;
}
describe('Some scenario', () => {
const getServer = useFakeServer();
it('accesses the server', () => {
const server = getServer();
// Test as you normally would..
expect(server.requests[0]. /* ... */);
});
});
If you need a script to run before all your test files, you can use globalSetup
This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites.
in your jest.config.js
//jest.config.js
module.exports = {
...
testTimeout: 20000,
globalSetup: "./setup.js"
};
then create a file named setup.js
// setup.js
module.exports = async () => {
console.log("I'll be called first before any test cases run");
//add in what you need to do here
};
Docs
You can move your beforeAll logic into one file and reference it in jest.config.js setupFilesAfterEnv section:
module.exports = {
...
setupFilesAfterEnv: ['<rootDir>/testHelper.ts'],
...
}
https://jestjs.io/docs/en/configuration#setupfilesafterenv-array
Create a function somewhere like so:
export function setupBeforeAndAfter(putParamsHereIfYouHaveAny) {
beforeAll(() => shared-before-all-code);
afterAll(() => shared-after-all-code);
beforeEach(() => shared-before-each-code);
afterEach(() => shared-after-each-code);
}
Then just call it wherever you would otherwise have manually written these functions:
describe('My test', () => {
setupBeforeAndAfter(putParamsHereIfYouHaveAny)
it('is amazing', () => {
// Stuff in setupBeforeAndAfter() will run before/after this test as appropriate
})
})
so i have a test file with multiple describes.
import 'babel-polyfill'
import { expect } from 'chai'
import app from '../server/server'
import request from 'supertest'
const req = request(app);
describe('GET: /api/user', function () {
it('should return valid user', function (done) {
req
.get('/api/user/' + userId)
.expect(200)
.end(function (err, res) {
...
...
done();
});
});
});
...
describe('...', function () { ... });
i want to have separate file for each test, but i don't want to keep writing the imports for each file... any idea? (i have more includes, the code above is just a sample)
maybe there is a way to have an index file that runs first and then includes other files recursively?
thanks!
It is better to have separate file for each test cases. But atlast include it in a common file like, test.js. and run the mocha test case on it.
For example,
test/lib/file1.js
test/lib/file2.js
test/lib/file3.js
test/lib/file4.js
test/lib/file5.js
Have a common file in the root of test folder,
and include all the files.
require('./lib/file1.js');
require('./lib/file2.js');
require('./lib/file3.js');
require('./lib/file4.js');
require('./lib/file5.js');
and run as
mocha test/test.js
It will run all the test case for you.