Currently, I can only detect development and production environments by .gitignoreing my config file, where I can explicitly set either process.env.ENV = 'prod' or process.env.ENV = 'devel' depending on the current environment. And I can be sure that this value will be imported when arangod starts.
But unit tests are not running on arangod startup, so maybe I need to hook up some listener to http requests or something to determine whether it's a request that fires unit tests? Can you help me with this plz?
Thank you
I found two possible solutions:
1) Create a pre-initializing file for tests e.g. test/init.js with the following content:
process.env.ENV = 'test'
then add it to your "tests" array in manifest.json as the very first entry point for tests:
"tests": [
"test/init.js",
"**/__tests__/**/*.js"
]
Detect test environment in any part of your code with:
if (process.env.ENV === 'test') { ... }
2) Since tests are running with Mocha we could also use a quick solution:
if (typeof it === 'function' && typeof describe === 'function') {
// code for Mocha environment only
}
Related
While in development we occasionally use skip or only to debug a particular test or test suit. Accidentally, we might forget to revert the cases and push the code for PR. I am looking for a way to detect or automatically run all tests even for skip and only tests in our CI pipeline(using Github action). It can be in either case as follow.
Fail the test when there are skip or only tests.
Run all tests even for skip and only.
Very much appreciate any help.
I came up with a solution for the second part of the question about running all tests even for skip and only. I don't think it's elegant solution, but it works and it's easy to implement.
First of all you need to change test runner to jest-circus if you work with jest bellow 27.x version. We need it so our custom test environment will use handleTestEvent function to watch for setup events. To do so, install jest-circus with npm i jest-circus and then in your jest.config.js set testRunner property:
//jest.config.js
module.exports = {
testRunner: 'jest-circus/runner',
...
}
From Jest 27.0 they changed default test runner to jest-circus so you can skip this step if you have this or higher version.
Then you have to write custom test environment. I suggest to write it based on jsdom so for example we also have access to window object in tests and etc. To do so run in terminal npm i jest-environment-jsdom and then create custom environment like so:
//custom-jsdom-environment.js
const JsDomEnvironment = require('jest-environment-jsdom')
class CustomJsDomEnvironment extends JsDomEnvironment {
async handleTestEvent(event, state) {
if(process.env.IS_CI === 'true' && event.name === 'setup') {
this.global.describe.only = this.global.describe
this.global.describe.skip = this.global.describe
this.global.fdescribe = this.global.describe
this.global.xdescribe = this.global.describe
this.global.it.only = this.global.it
this.global.it.skip = this.global.it
this.global.fit = this.global.it
this.global.xit = this.global.it
this.global.test.only = this.global.test
this.global.test.skip = this.global.test
this.global.ftest = this.global.test
this.global.xtest = this.global.test
}
}
}
module.exports = CustomJsDomEnvironment
And inform jest to properly use it:
//jest.config.js
module.exports = {
testRunner: 'jest-circus/runner',
testEnvironment: 'path/to/custom/jsdom/environment.js',
...
}
Then you just have to setup custom environment value IS_CI in your CI pipeline and from now on all your skipped tests will run.
Also in custom test environment you could watch for skipped test and throw an error when your runner find skip/only. Unfortunately throwing an error in this place won't fail a test. You would need to find a way to fail a test outside of a test.
//custom-jsdom-environment.js
const JsDomEnvironment = require('jest-environment-jsdom')
const path = require('path')
class CustomJsDomEnvironment extends JsDomEnvironment {
constructor(config, context) {
super(config, context)
const testPath = context.testPath
this.testFile = path.basename(testPath)
}
async handleTestEvent(event, state) {
if(process.env.IS_CI === 'true' && event.name === 'add_test') {
if(event.mode === 'skip' || event.mode === 'only') {
const msg = `Run ${event.mode} test: '${event.testName}' in ${this.testFile}`
throw new Error(msg)
}
}
}
}
module.exports = CustomJsDomEnvironment
I am using Jest for testing. How do I access the filename or file path of the current test being run?
I need a conditional statement that runs different lines of code based on whether it is a unit test file or integration test file.
Here is an example of what I am trying to achieve:
beforeAll(() => {
if (integration_test_file){
// run this this code
} else if (unit_test_file){
// run this code instead
}
})
This information is available in Jest environment. This is the case for custom environment:
const Environment = require('jest-environment-node'); // or jest-environment-jsdom
module.exports = class MyEnvironment extends Environment {
constructor(config, context) {
super(config, context);
this.testPath = context.testPath;
}
async setup() {
await super.setup();
this.global.IS_INTEGRATION = /match integration/.test(this.testPath);
}
}
The environment is instantiated for each test suite, testPath contains full path to current test file.
IS_INTEGRATION global variable will be available in setupFilesAfterEnv and tests themselves. In case the code needs to be evaluated for all tests, it may belong to environment setup and teardown methods.
You can access the name of the file path of the test being ran in jest via its global variables.
the file path can be found under the global variable
jasmine.testPath
or
global.jasmine.testPath
this answer only applies if you're using jest with its default test runner "jasmine" or "jasmine2". results will differ based on the test runner you use. see
https://jestjs.io/docs/en/configuration#testrunner-string
__dirname works a treat (ref: https://www.geeksforgeeks.org/how-to-get-the-path-of-current-script-using-node-js/)
I've written a node module with UMD (Universal Module Definition) Pattern to make it compatible with amd and plain javascript as well.
The definition is as below
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.myModule = factory();
}
}(this, function (global) {
...
return {};
}));
With mocha & chai the tests are running fine, the only issue is that since the tests are executed from within node the code coverage for define(factory); and root.myModule = factory(); lines is showing red.
Just wanted to know if there is any way(maybe a hack) to get 100% coverage for this code with mocha chai.
Sure!
How coverage tools for JS basically work, is that their injecting little bit of tracker code for each line. So what you need is that the execution flow wonders there.
Since this is an if-else logic, to get 100% you will be needing multiple test scenarios to cover this.
Note: it's also important for actually achieving this effect, that loading that file (factory) should happen after every scenario setup, because that is when those module-loading-if-else lines are executed. Now this depends on what is your file/module loading in your test execution environment.
Now to enter into that branch of the if-else create a test scenario where you can just enforce the existence of that define function with a few lines like (e.g. in a beforeEach section):
define = function () {
console.log('hello I am a fake define, but I will be defined ...');
console.log(' ... so that if else branch will be executed.');
}
// and of course the needed .amd property.
define.amd = true;
I think the same weird stuff can be done w/ the exports variable, but it might be hard to achieve if you are just pure CommonJS require-ing that file, since require-ing defines the exports variable for that file context, so maybe you could modify your production code for this weird case. I wouldn't do that, but you said any way:
...
} else if (typeof exports === 'object' && testExportsEnabled) {
...
Now using this you can just have one scenario where in beforeEach you do: testExportsEnabled = true; and w/ this being false, the last branch should be executed.
Of course you can execute all branches with such a simple trick! Now to have no code modification AND trigger the third branch (basically the case for simple script-tag-loading in browser), you will need to dig in into your module loading code and create some hacks there, but that is a bit too much for me in an SO question! :]
Take care!
I am using Gulp / Browserify / Node on Windows, and I want to only include debugging information when in development.
I have a dependant task that is being run before anything else
gulp.task('set-dev-node-env', function() {
process.env.NODE_ENV = 'development'
}
Yet when I try and access this in my code I am finding process.env is an empty object.
console.log("process.env",process.env)
How can I get this to work?
I found a solution was to use envify
var envify = require('envify/custom')
and add it as a transform on my browserify() call
.transform(envify({
NODE_ENV: 'development'
}))
I have an application that has some development-specific debugging code in it. Currently, all development code is guarded by a variable called dev at the top of the file. Here's an example of what my app does:
var dev = true;
if (dev) {
console.log("Hello developer");
} else {
console.log("Hello production");
}
When I go to deploy my application, I have to manually change the dev variable form true to false. This sucks.
I'm in the middle of migrating from hand-rolled builds to gulp.js and I want to solve this development vs. production build problem cleanly. I'm thinking about the following:
// Inside main.js
var dev = require('./isdev');
if (dev) //...
// Inside isdev.js:
module.exports = true;
Now, when I build for production, instead of manually setting the dev flag to false, I want to replace isdev.js from module.exports = true; to module.exports = false;. My specific question is, how do I automate gulp such that gulp development produces a file with dev = true and gulp production produces a file with dev = false.
Here's an update to those who are curious.
First, I have an options.js:
exports.dev = false;
I also have a options_dev.js:
exports.dev = true;
Inside of gulpfile.js, I have the following code that parses input arguments:
// Parse the arguments. Use `gulp --prod` to build a production extension
var argv = parseArgs(process.argv.slice(2));
var dev = !argv['prod']; // Whether to build a development extension or not
Finally, when I pipe to browserify, I have the following:
var resolve = require('browser-resolve');
// ...
.pipe(browserify({
debug: dev,
resolve: function(pkg, opts) {
// Replace options.js with options_dev.js if this is a dev build
if (dev) {
opts.modules['./options'] = 'src/options_dev.js';
}
return resolve.apply(this, arguments);
}
}))
The magic happens by using a custom resolve function, dynamically swapping ./options with options_dev for development builds. The browserify docs say:
You can give browserify a custom opts.resolve() function or by default it uses browser-resolve.
When we run gulp, we build a development version. When we run gulp --prod, we build a production version. The value of require('./options').dev allows us to dynamically change things like server endpoints, etc. Cool!
The way that I've seen this done is to set the environment variable on the command line before the execution command. An example of doing this with the Node.JS CLI (in a bash-like environment) would be:
ENV=dev node
> process.env.ENV
'dev'
Then in your code, you could do:
var dev = process.env.ENV === 'dev'
So with gulp, you could use:
ENV=dev gulp <task name>
I tested this out with the following snippet, and it works:
gulp.task('dev', function(){
if (process.env.ENV === 'dev')
console.log("IT WORKED");
else
console.log("NO DICE");
});
Edit:
You can write out the environment to the file isdev right before building:
var fs = require('fs');
gulp.task('build', function(){
if (process.env.ENV === 'dev')
fs.writeFileSync('isdev', 'module.exports = true');
else
fs.writeFileSync('isdev', 'module.exports = false');
// kick off build
});
Now, the correct value will be present in isdev for any require call in the built bundle. You could extend this to other specified environments as well (or to other configuration flags).