Jest toMatchSnapshot not throwing an exception - jestjs

Most of Jest's expect(arg1).xxxx() methods will throw an exception if the comparison fails to match expectations. One exception to this pattern seems to be the toMatchSnapshot() method. It seems to never throw an exception and instead stores the failure information for later Jest code to process.
How can we cause toMatchSnapshot() to throw an exception? If that's not possible, is there another way that our tests can detect when the snapshot comparison failed?

This will work! After running your toMatchSnapshot assertion, check the global state: expect(global[GLOBAL_STATE].state.snapshotState.matched).toEqual(1);
Just spent the last hour trying to figure it out for our own tests. This doesn't feel hacky to me either, though a maintainer of Jest may be able to tell me whether accessing Symbol.for('$$jest-matchers-object') is a good idea or not. Here's a full code snippet for context:
const GLOBAL_STATE = Symbol.for('$$jest-matchers-object');
describe('Describe test', () => {
it('should test something', () => {
try {
expect({}).toMatchSnapshot(); // replace with whatever you're trying to test
expect(global[GLOBAL_STATE].state.snapshotState.matched).toEqual(1);
} catch (e) {
console.log(`\x1b[31mWARNING!!! Catch snapshot failure here and print some message about it...`);
throw e;
}
});
});

If you run a test (e.g. /Foobar.test.js) which contains a toMatchSnapshot matcher jest by default will create a snapshot file on the first run (e.g. /__snapshots__/Foobar.test.js.snap).
This first run that creates the snapshot will pass.
If you want the test to fail you need to commit the snapshot alongside with your test.
The next test builds will compare the changes you make to the committed snapshot and if they differ the test will fail.
Here is the official link to the Documentation on 'Snapshot Testing' with Jest.

One, less than ideal, way to cause toMatchSnapshot to throw an exception when there is a snapshot mismatch is to edit the implementation of toMatchSnapshot. Experienced Node developers will consider this to be bad practice, but if you are very strongly motivated to have that method throw an exception, this approach is actually easy and depending on how you periodically update your tooling, only somewhat error-prone.
The file of interest will be named something like "node_modules/jest-snapshot/build/index.js".
The line of interest is the first line in the method:
const toMatchSnapshot = function (received, testName) {
this.dontThrow && this.dontThrow(); const
currentTestName = ....
You'll want to split that first line and omit the calling of this.dontThrow(). The resulting code should look similar to this:
const toMatchSnapshot = function (received, testName) {
//this.dontThrow && this.dontThrow();
const
currentTestName = ....
A final step you might want to take is to send a feature request to the Jest team or support an existing feature request that is of your liking like the following: link

Related

Node.js When updating a document failed, is best to throw an exception or returning information?

I have an updateDocument method in a class for a service layer in a node.js, express, mongoose application. I'm wondering what is the best practice for handing cases where the update method didn't update a document, for example if the wrong id is passed in.
Version 1: If the update wasn't successful, return an object with success: false:
async updateDocument(id, updates) {
const output = await this.DocumentModel.updateOne({ _id: id }, updates);
let message = 'Something went wrong';
let success = false;
let updatedItem = null;
if (output.nModified) {
message = 'Successfully updated document.';
success = true;
updatedItem = await this.getDocument(id);
}
return { message, success, updatedItem};
}
Version 2: If the update wasn't successful, throw an error:
async updateDocument(id, updates) {
const output = await this.DocumentModel.updateOne({ _id: id }, updates);
let updatedItem;
if (output.nModified) {
updatedItem = await this.getDocument(id);
} else{
throw new Error("The document wasn't updated")
}
return updatedItem;
}
Do you always throw an exception when the input, such as a bad id, isn't correct? Or could you return information about the update being a success or not? As newbie node.js developer, I'm not sure I am grasping the full picture enough to recognize problems with either method.
There is no golden way, only principles that lead to robust and well-maintainable software.
Generally, you should use a try-catch-statement for all kinds of errors that are not in your control (connections, disk space, credentials, ...) . The errors should then be handled as soon as possible, but not before. The reason for this is that you often don't know, yet, how to handle an error in an appropriate manner at a lower layer.
For logical "errors" that you can expect (wrong input format, missing username, unknown options, ...), you should use an if-statement or a validation function and then throw an error, if anything is not as expected.
In your case, you should check, if the methods updateOne or getDocument can throw errors. If yes, you should wrap these functions with a try-catch-statement.
A few more tips:
Both versions of your code are good. But I would prefer version 2 because it is more concise.
If you are sure that there is always an output object, you can destruct the nModified property like this:
const { nModified } = await this.DocumentModel.updateOne({ _id: id }, updates);
If you use a negative if-statement, you can reduce the depth of indentation and you can use const variables:
if (!nModified) {
throw new Error("The document wasn't updated")
}
const updatedItem = await this.getDocument(id);
Now, you could also directly return this.getDocument(id) and don't need the variable updatedItem anymore.
You can finally handle your errors in your controller classes.
You can use custom error classes to be consistent in your error handling and error messages all over your app.
I hope this is at least a bit helpful.
References
These are some similar questions with good answers. But you need to take care because many code examples are not in modern JavaScript.
A general discussion about the pros and cons of Error-Handling vs.
if-else-statements is done here:
What is the advantage of using try {} catch {} versus if {} else {}
Error-Handling in Node.js is discussed here in this thread:
Node.js Best Practice Exception Handling
It seemed like there were a lot of different opinions on this and not one go-to method. Here's some information I found and what I ended up doing.
When to throw an exception?
Every function asks a question. If the input it is given makes that question a fallacy, then throw an exception. This line is harder to draw with functions that return void, but the bottom line is: if the function's assumptions about its inputs are violated, it should throw an exception instead of returning normally.
Should a retrieval method return 'null' or throw an exception when it can't produce the return value?
Answer 1:
Whatever you do, make sure you document it. I think this point is more important than exactly which approach is "best".
Answer 2:
If you are always expecting to find a value then throw the exception if it is missing. The exception would mean that there was a problem.
If the value can be missing or present and both are valid for the application logic then return a null.
More important: What do you do other places in the code? Consistency is important.
Where should exceptions be handled?
Answer 1: in the layer of code that can actually do something about the error
Exceptions should be handled in the layer of code that can actually do something about the error.
The "log and rethrow" pattern is often considered an antipattern (for exactly the reason you mentioned, leads to a lot of duplicate code and doesn't really help you do anything practical.)
The point of an exception is that it is "not expected". If the layer of code you are working in can't do something reasonable to continue successful processing when the error happens, just let it bubble up.
If the layer of code you are working in can do something to continue when the error happens, that is the spot to handle the error. (And returning a "failed" http response code counts as a way to "continue processing". You are saving the program from crashing.)
-source: softwareengineering.stackexchange
Answer 2: Handle errors centrally, not within a middleware
Without one dedicated object for error handling, greater are the chances of important errors hiding under the radar due to improper handling. The error handler object is responsible for making the error visible, for example by writing to a well-formatted logger, sending events to some monitoring product like Sentry, Rollbar, or Raygun. Most web frameworks, like Express, provide an error handling middleware mechanism. A typical error handling flow might be: Some module throws an error -> API router catches the error -> it propagates the error to the middleware (e.g. Express, KOA) who is responsible for catching errors -> a centralized error handler is called -> the middleware is being told whether this error is an untrusted error (not operational) so it can restart the app gracefully. Note that it’s a common, yet wrong, practice to handle errors within Express middleware – doing so will not cover errors that are thrown in non-web interfaces.
-source; Handle errors centrally, not within a middleware
More: Best Practice Node.js: Error Handling
So it seems like these two principles disagree. #1 says to handle it right away if you can. So for me it would be in the service layer. But the #2 says handle it centrally, like in the server file. I went with #2.
My decision: throw the error in a custom error class
It combined a few methods people suggested. I am throwing the error, but I'm not "log and rethrow"-ing, as the answer above warned against. Instead, I put the error in a custom error with more information and throw that. It is logged and handled centrally.
So first in my service layer this is how an error is thrown:
async addUser(user) {
let newUser;
try {
newUser = await this.UserModel.create(user);
} catch (err) {
throw new ApplicationError( // custom error
{
user, // params that are useful
err, //original error
},
`Unable to create user: ${err.name}: ${err.message}` // error message
);
}
return newUser;
}
ApplicationError is a custom error class that takes an info object and a message. I got this idea from here:
In this pattern, we would start our application with an ApplicationError class this way we know all errors in our applications that we explicitly throw are going to inherit from it. So we would start off with the following error classes:
-source: smashingmagazine
You could put other helpful information in your custom error class, even maybe what EJS template to use! So you could really handle the error creatively depending on how you structure your custom error class. I don't know if that's "normal", maybe it's not SOLID to include the EJS template, but I think it's an interesting concept to explore. You could think about other ways that might be more SOLID to dynamically react to errors.
This is the handleError file for now, but I will probably change it up to work with the custom error to create a more informative page. :
const logger = require("./logger");
module.exports = (err, req, res, next) => {
if (res.headersSent) {
return next(err);
}
logger.log("Error:", err);
return res.status(500).render("500", {
title: "500",
});
};
Then I add that function to my server file as the last middleware:
app.use(handleError);
In conclusion, it seems like there's a bit of disagreement on how to handle errors though it seems more people think you should throw the error and probably handle it centrally. Find a way that works for you, be consistent, and document it.

Is there any way to add callbacks to Jest when all tests succeed or fail?

I'd like to run a callback when all the tests in a describe block pass (or fail), is there some hook or something in the Jest API to do this? I could not find anything applicable in the docs.
I'm making several API requests to collect data in order to compare it to data in a CSV file, in order to diff the contents. When the tests have all passed, I would like to save all the API responses in a file, therefore I need some sort of 'all tests passed' callback
You can run jest programmatically. Note that this approach is "hack" because there is no official support for running jest like this.
see: https://medium.com/web-developers-path/how-to-run-jest-programmatically-in-node-js-jest-javascript-api-492a8bc250de
There is afterAll that is aware of describe but runs regardless of test results. It can be used as a part of function to aggregate data from tests:
let responses;
testAndSaveResponses((name, fn) => {
if (!responses) {
responses = [];
} else {
afterAll(async () => {
if (!responses.includes(null)) {
// no errors, proceed with response processing
}
});
}
test(name, async () => {
try {
responses.push(await fn());
} catch (err) {
responses.push(null);
throw err;
}
});
});
It's supposed to be used instead of Jest test and be enhanced to support multiple describe scopes.
There is custom environment. Circus runner allows to hook test events, finish_describe_definition in particular. It is applied to all tests, unaware of custom data (e.g. responses that need to be saved) and should interact with them through global variables.
There is custom reporter, it receives a list of passed and failed tests. It is applied to all tests, unaware of custom data defined in tests and doesn't have access to globals from test scope so cannot be used to collect responses.

Why does this simple Node, Sequelize Promise code hang?

I'm trying to do a simple command line database transformation with node.js and sequelize. I've simplified my errant code down to the following, but it never returns:
// Set up database connection and models
var models = require('../models_sequelize');
models.User.findOne()
.then(a => {
console.log(a.name);
});
I get a name printed, but then the script hangs. What is wrong? How do I debug this to see what's stuck? I get the impression that there's an orphan promise that's not being fulfilled, but I don't understand where or why. I must be missing something obvious.
If I run the same interactively from the node console, it returns fine.
Sirko's comment re: close() gave me something to go on. I can stop the hanging with the following code:
var models = require('../models_sequelize');
models.User.findOne()
.then(a => {
console.log(a.name);
models.sequelize.close();
})
Alternatively, this seems to work too as I guess it's doing exactly the same thing:
var models = require('../models_sequelize');
models.User.findOne()
.then(a => {
console.log(a.name);
})
.finally(() => {
models.sequelize.close();
});
I also found something about connection pooling timeouts, but I don't think that affects my simple use case. I imagine it'll come into play in more complicated examples.
Would still like to find a good reference as to why this is necessary rather than just my guess.

Testing Involving Database

well before specifying my problem, i want to tell that i'm new to the field of testing, so here is my problem:
i developed a rest api using express + sequelize(mysql), and i want to write some test for my api. i choosed to use jasmine library for testing.
so right know i want to test the create and update rest endpoint, i will need access to a database, but the problem is that the test cases are run in parallel, and there is only one database, so if i want to delete all item from a table in a test case, and another test case have create a row in that table, there will be a problem.
const request = require('superagent');
const models = require('../../src/models');
const Station = models.Station;
describe("station testing", function () {
before(() => {
// delete and recreate all database table
// before running any test
});
describe("crud station", function () {
it('should create model', () => {
Station.create({
'name': 'test',
lat: 12,
long: 123,
}).then( model => {
expect(model).toBeTruthy();
});
});
it('should delete evrything', () => {
Station.deleteAll().then( () => {
// problem here if after the first model is created and before create model except is executed
expect(Station.Count()).toEqual(0);
}
});
});
});
Your problem is that you are not writing unit tests here.
You need to understand the most important rule of unit testing - only test one unit at a time. A unit can be thought of as an area of your code. In a traditional desktop project (Java, C#, etc), a unit would be one class. In the case of Javascript, a unit is harder to define, but it certainly will only include the Javacript. If you are including any server code (for example, the database) in your tests, then you are not unit testing, you are doing integration testing (which is also very important, but much harder).
Your Javascript will have dependencies (ie other code that it calls, say via Ajax calls), which in your case will include the server code that is called. In order to unit test, you need to make sure that you are only testing the Javascript, which means that when running the tests, you don't want the server code to be called at all. That way, you isolate any errors in that unit of code, and can be confident that any problems found are indeed in that unit. If you include other units, then it could be the other units that have the problem.
In a strongly-typed language (like Java, C#, etc), there are frameworks that allow you to set up a mock for each dependency. Whilst I haven't tried any myself (that's this week's job), there are mocking frameworks for Javascript, and you would probably need to use one of them to do real unit testing. You mock out the server code, so when you run the test, it doesn't actually hit the database at all. Apart from solving your problem, it avoids a whole load of other issues that you will likely hit at some point with your current approach.
If you don't want to use a mocking framework, one other way to do it is to change your Javascript so that the function you are testing takes an extra parameter, which is a function that does the actual server call. So, instead of...
deleteCustomer(42);
deleteCustomer(id) {
validate(id);
$.ajax(...);
}
...your code would look like this...
deleteCustomer(42, callServer);
deleteCustomer(id, serverCall) {
validate(id);
serverCall(id);
}
...where serverCall() contains the Ajax call.
Then, to unit test, you would test something like this...
deleteCustomer(42, function(){});
...so that instead of calling the server, nothing is actually done.
This is obviously going to require some rewriting of your code, which could be avoided by mocking, but would work. My advice would be to spend some time learning how to use a mocking framework. It will pay off in the long run.
Sorry this has been a bit long. Unfortunately, you're getting into a complex area of unit testing, and it's important to understand what you're doing. I strongly recommend you read up about unit testing before you go any further, as a good understanding of the basics will save you a lot of trouble later on. Anything by Robert Martin (aka Uncle Bob) on the subject will be good, but there are plenty of resources around the web.
Hope this helps. If you want any more info, or clarification, ask away.
Jasmine supports a function for beforeEach which run before each spec in a describe block.
You can use that.
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() { foo += 1; });
afterEach(function() { foo = 0; });
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1)
expect(true).toEqual(true);
});
});
So you could let the beforeEach take care of the delete operation.

How to detect if a mocha test is running in node.js?

I want to make sure that in case the code is running in test mode, that it does not (accidentally) access the wrong database. What is the best way to detect if the code is currently running in test mode?
As already mentioned in comment it is bad practice to build your code aware of tests. I even can't find mentioned topic on SO and even outside.
However, I can think of ways to detect the fact of being launched in test.
For me mocha doesn't add itself to global scope, but adds global.it.
So your check may be
var isInTest = typeof global.it === 'function';
I would suggest to be sure you don't false-detect to add check for global.sinon and global.chai which you most likely used in your node.js tests.
Inspecting process.argv is a good approach in my experience.
For instance if I console.log(process.argv) during a test I get the following:
[
'node',
'/usr/local/bin/gulp',
'test',
'--file',
'getSSAI.test.unit.js',
'--bail',
'--watch'
]
From which you can see that gulp is being used. Using yargs makes interpretting this a whole lot easier.
I strongly agree with Kirill and in general that code shouldn't be aware of the fact that it's being tested (in your case perhaps you could pass in your db binding / connection via a constructor?), for things like logging I can see why you might want to detect this.
Easiest option is to just use the detect-mocha [NPM package.
var detectMocha = require('detect-mocha');
if(detectMocha()) {
// doSomethingFancy
}
If you don't want to do that, the relevant code is just
function isMochaRunning(context) {
return ['afterEach','after','beforeEach','before','describe','it'].every(function(functionName){
return context[functionName] instanceof Function;
})
Where context is the current window or global.
I agreed with #Joshua on his answer, he says Inspecting process.argv is a good approach in my experience.
So, I've written a simple detecting mocha code.
const _MOCHA_PATH = new RegExp('(\\\\|/)node_modules\\1mocha\\1bin\\1_mocha$');
var isMochaRunning = process.argv.findIndex(arg => _MOCHA_PATH.test(arg)) > -1;
In a small project with no logging infrastructure, I use
if (process.env.npm_lifecycle_event !== 'test')
console.error(e);
to avoid logging expected errors during testing, as they would interfere with test output.

Resources