Mocha how to use utils function stackTraceFilter() - node.js

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){});

Related

Move common mocking code to a separate file containing a Jest manual mock

There is a mock I use in many places, so I want to move it into a separate file that can be reused.
I think Jest calls this a "manual mock". However I don't want to use the __mocks__ convention.
The top of the file being tested:
import * as dotenvSafe from "dotenv-safe";
The manual mock file:
const dotenvSafe: any = jest.genMockFromModule("dotenv-safe");
dotenvSafe.load = jest.fn(() => { // the function I want to mock
return {
error: undefined,
parsed: [],
};
});
export default dotenvSafe;
At the top of the test file, I tried various things:
jest.setMock("dotenv-safe", "../../mocks/dotenv-safe");
Doesn't work. The code being tested gets "../../mocks/dotenv-safe.mock" instead of a module.
jest.mock("dotenv-safe", () => require("../../mocks/dotenv-safe"));
Doesn't work - The code being tested throws TypeError: dotenvSafe.load is not a function.
jest.mock("dotenv-safe", () => { return { load: jest.fn(() => ({error: undefined, parsed: []})) }; });
Does work! But the mock is inline, and I want to move it to a separate file. I don't want to repeat this in every file.
What is the correct syntax?
require("../../mocks/dotenv-safe") equals to module exports. It's default export that is used, so it should be:
jest.mock("dotenv-safe", () => require("../../mocks/dotenv-safe").default);

Stub an export from a native ES Module without babel

I'm using AVA + sinon to build my unit test. Since I need ES6 modules and I don't like babel, I'm using mjs files all over my project, including the test files. I use "--experimental-modules" argument to start my project and I use "esm" package in the test. The following is my ava config and the test code.
"ava": {
"require": [
"esm"
],
"babel": false,
"extensions": [
"mjs"
]
},
// test.mjs
import test from 'ava';
import sinon from 'sinon';
import { receiver } from '../src/receiver';
import * as factory from '../src/factory';
test('pipeline get called', async t => {
const stub_factory = sinon.stub(factory, 'backbone_factory');
t.pass();
});
But I get the error message:
TypeError {
message: 'ES Modules cannot be stubbed',
}
How can I stub an ES6 module without babel?
According to John-David Dalton, the creator of the esm package, it is only possible to mutate the namespaces of *.js files - *.mjs files are locked down.
That means Sinon (and all other software) is not able to stub these modules - exactly as the error message points out. There are two ways to fix the issue here:
Just rename the files' extension to .js to make the exports mutable. This is the least invasive, as the mutableNamespace option is on by default for esm. This only applies when you use the esm loader, of course.
Use a dedicated module loader that proxies all the imports and replaces them with one of your liking.
The tech stack agnostic terminology for option 2 is a link seam - essentially replacing Node's default module loader. Usually one could use Quibble, ESMock, proxyquire or rewire, meaning the test above would look something like this when using Proxyquire:
// assuming that `receiver` uses `factory` internally
// comment out the import - we'll use proxyquire
// import * as factory from '../src/factory';
// import { receiver } from '../src/receiver';
const factory = { backbone_factory: sinon.stub() };
const receiver = proxyquire('../src/receiver', { './factory' : factory });
Modifying the proxyquire example to use Quibble or ESMock (both supports ESM natively) should be trivial.
Sinon needs to evolve with the times or be left behind (ESM is becoming defacto now with Node 12) as it is turning out to be a giant pain to use due to its many limitations.
This article provides a workaround (actually 4, but I only found 1 to be acceptable). In my case, I was exporting functions from a module directly and getting this error: ES Modules cannot be stubbed
export function abc() {
}
The solution was to put the functions into a class and export that instead:
export class Utils {
abc() {
}
}
notice that the function keyword is removed in the method syntax.
Happy Coding - hope Sinon makes it in the long run, but it's not looking good given its excessive rigidity.
Sticking with the questions Headline „Stub an export from a native ES Module without babel“ here's my take, using mocha and esmock:
(credits: certainly #oligofren brought me on the right path…)
package.json:
"scripts": {
...
"test": "mocha --loader=esmock",
"devDependencies": {
"esmock": "^2.1.0",
"mocha": "^10.2.0",
TestDad.js (a class)
import { sonBar } from './testSon.js'
export default class TestDad {
constructor() {
console.log(purple('constructing TestDad, calling...'))
sonBar()
}
}
testSon.js (a 'util' library)
export const sonFoo = () => {
console.log(`Original Son 'foo' and here, my brother... `)
sonBar()
}
export const sonBar = () => {
console.log(`Original Son bar`)
}
export default { sonFoo, sonBar }
esmockTest.js
import esmock from 'esmock'
describe.only(autoSuiteName(import.meta.url),
() => {
it('Test 1', async() => {
const TestDad = await esmock('../src/commands/TestDad.js', {
'../src/commands/testSon.js': {
sonBar: () => { console.log('STEPSON Bar') }
}
})
// eslint-disable-next-line no-new
new TestDad()
})
it('Test 2', async() => {
const testSon = await esmock('../src/commands/testSon.js')
testSon.sonBar = () => { console.log('ANOTHER STEPSON Bar') }
testSon.sonFoo() // still original
testSon.sonBar() // different now
})
})
autoSuiteName(import.meta.url)
regarding Test1
working nicely, import bended as desired.
regarding Test1
Bending a single function to do something else is not a problem.
(but then there is not much test value in calling your very own function you just defined, is there?)
Enclosed function calls within the module (i.e. from sonFoo to sonBar) remain what they are, they are indeed a closure, still pointing to the prior function
Btw also tested that: No better results with sinon.callsFake() (would have been surprising if there was…)

Cannot mock filesystem in nodejs unit tests

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)
})
})

Jest beforeAll() share between multiple test files

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
})
})

Can't figure out where to put require.config when using TypeScript, RequireJs, and Jasmine

I've been following the pattern for setting up TypeScript, RequireJS, and Jasmine that Steve Fenton describes here:
https://www.stevefenton.co.uk/Content/Blog/Date/201407/Blog/Combining-TypeScript-Jasmine-And-AMD-With-RequireJS/
That pattern as really worked well and truly unblocked me (yay!), but I'm now at the point where I need to customize some settings for RequireJS but I can't seem to figure out where to put my require.config call. Everywhere I've tried has caused breaks and regressions. Here are the two approaches that seem most logical/promising
In SpecRunner.cshtml
<script data-main="/Scripts/TypeScript/RequireJsConfig" src="/Scripts/require.js"></script>
In RequireJsConfig.ts
require.config({
baseUrl: "../Scripts",
paths: {
jquery: "../jquery-2.1.3"
}
});
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Attempt 1: When I try it this way I immediately get this error
//
// JavaScript runtime error: Object doesn't support property or method 'config'
//
import TestLoader = require("Tests/TestLoader");
TestLoader.Run();
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Attempt 2: When I try it this way, everything builds and runs without errors, but
// Jasmine doesn't find any of the tests. All I get is "No specs found" even
// though I see the breakpoints on my "it" statements getting hit.
//
require(["Tests/TestLoader"], (testLoader) => {
testLoader.Run();
});
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
jasmine.getEnv().execute();
In TestLoader.ts
import GuidHelperTests = require("Tests/T3/Helpers/GuidHelperTests");
import ObjectHelperTests = require("Tests/T3/Helpers/ObjectHelperTests");
class TestLoader {
public static Run: () => void = () => {
GuidHelperTests.Run();
ObjectHelperTests.Run();
}
}
export var Run = () => TestLoader.Run();
In GuidHelperTests.ts
import T3 = require("T3/T3Lib");
export var Run = () => {
describe("GuidHelper tests", () => {
it("GUID validator validates good GUID", () => {
// etc. ...
My guess is that Attempt 2 doesn't work because of some kind of sequencing issue where the test discovery process is happening before modules are loaded, or something like that. I'm just not versed enough in RequireJS to know what my options are here.
I prefer to keep my configuration away from my application - you can pre-register the configuration like this, and it will be picked up by RequireJS when it loads. No need to add it to your first file.
<script>
var require = {
baseUrl: "../Scripts",
paths: {
jquery: "../jquery-2.1.3"
}
};
</script>
<script data-main="/Scripts/TypeScript/RequireJsConfig" src="/Scripts/require.js"></script>

Resources