Difference between mock and unmock test in Jest - javascript

Recently I've become familiar with Jest library and unit test concepts and everything due to jest's documentation is right in my codes.
But I need to know what's difference between mocking and unmocking concept in Jest and other unit test libraries.
Thanks

Mock means to replace an instance with another one. In jest its used to replace the implementation of imported modules with your own one.
jest.mock('yourModule', () => {test: ()=> 'test'})
The main idea behind it, is to isolate your code in unit test, so that you only test one module without the influence of other parts of your application or external code. This has a bunch advantages. First of all if the code in one module breaks, only the test for this part will fail and not a all test for parts that just import this module. Second you can simplify the test itself as you dont need to start up a server that returns with specific data, which would also slow down your code.
The unmock feature is there cause of the automock feature, which was the default in the past. Automocking will replace all imported modules with default mock. As this make sense for some modules but is not wanted for lodash for example you could then unmock the mocking of them. So unmock is mostly needed with automock switched on to get the original implementation if needed.

Related

jest is failing to find `bota` and `atob`

Creating a jest test like:
test("btoa", () => {
expect(btoa("aaa")).toStrictEqual("YWFh");
});
fails with
ReferenceError: btoa is not defined
however, node does define btoa as of node 16, and so the following:
console.log(bota("aaa"))
correctly outputs YWFh.
How can I configure jest to get this test to pass? Clearly something is happening in the jest test runner to not execute in the current node environment, or otherwise is stripping specific built-ins, only I can't seem to find any documentation on how to debug or adjust this.
Update
There are work arounds by writing the encoding manually in "pure js" or depending on something that's similar, but I'm particularly interested in why the jest execution ending is failing to find built-ins that seem to be present in other environments.
This also works fine in other testing frameworks like mocha, so it's clearly related to the jest runner in particular.
Update
After much searching and head scratching as to why btoa/atob are available in node but NOT available in jest running in node, I finally figured it out. Jest runs all tests inside a vm, which is an isolated sandbox environment. The btoa/atob methods are not automatically exposed on the global object inside of a VM. Best explained by example:
const vm = require('vm');
// this works outside the vm - but for legacy reasons only
// you shouldn't be doing this in the first place
btoa('aaa'); // -> "YWFh"
const context = vm.createContext({});
const code = 'btoa("aaa")';
vm.runInContext(code, context); //-> Uncaught ReferenceError: btoa is not defined
Note: The answer described below is still the "solution" - you need to define these methods for use in node, and then you need to expose them using jest's globalSetup.
Original answer
The root of your problem is the fact that NodeJS and web browsers have different APIs. For example, I get this deprecation notice when I try to use btoa in my node application.
The first part of the solution is that you need to provide your own atob/btoa methods for use in NodeJs (see examples here). Then you need to make these available using jest's globalSetup config:
/** Encodes a string as base64 format */
global.btoa = (str: string) => Buffer.from(str, 'binary').toString('base64');
/** Decodes a base64 encoded string */
global.atob = (str: string) => Buffer.from(str, 'base64').toString('binary');
If you don't feel comfortable doing this yourself, there are libraries and tools out there that do it for you (jsdom, phantomjs, testing-library). These libraries essentially replicate the browser APIs in a node environment for doing things like running tests, server-side rendering, etc. I recommend reading about testing web frameworks for code examples and techniques.

Mocking trickier dependencies with Jest [namely nested dependencies based off of index files]

I've read throughthe other questions regarding mocking with Jest, but I don't really seem to get the hang of it, especially with more complex "replacements".
The thing is also that my dependency isnt just an "import 'X'", but for a more complex nested thingy.
const DefaultErrorHandler = require('common').utility.errorHandler.getNoConnectionHandler();
class ErrorHandler extends DefaultErrorHandler { ...
and it's that DefaultErrorHandler thats troubliung me, so I'd like to replace it with my far more manageable MockDefaultErrorHandler.
But how do I do that? No Documentation or other question seems to fit my case (and/or I'm missing some important point)
its not much different from regular mocking
In the test code'put:
const NoConnectionHandler = require('common/lib/utility/error/handler/NoConnectionHandler');
jest.mock('common-bon/lib/utils/error/handler/NoConnectionHandler');
Notice that this path is slightly different than the path i would have intuitively "created" from the require... line.
What saved me was that in the "common" file, the function "getNoConnectionHandler()" itself ends with a require(...)
And the path of that require there was the one I needed.

Debuggin with mocha, load globals

My goal is to debug one of my tests. I'm using Mocha as a base, and SinonJS for spies, stubs and mocks. For some unknown reason my stub of the ajax method has stopped working. It worked a week ago, now the requests are sent and the stub does not track the calls.
I have these lines inside the outermost describe
let sandbox = sinon.sandbox.create();
let ajaxStub = undefined;
and then this:
beforeEach(function () {
ajaxStub = sandbox.stub($, 'ajax');
});
afterEach(function () {
sandbox.restore();
});
Anyway, my question is not what's wrong with this, I'm probably doing something extremely stupid elsewhere, and some debugging could probably solve it. My problem is with the debugging itself.
mocha --debug-brk --inspect ./test/mytest.js
This is what I run in command line to get the debugging session going.
My problem is to run the tests I'm currently using Gulp, with which I'm loading all my framework dependencies and all my globals - the libraries added this way include also jQuery and sinon
And of course, if I debug my tests using that command line, NodeJS does not load the required files in the environment, and at the first reference to sinon I get an exception.
I could create an html page in which I load required files and tests and run the test - then debug it manually with the browser inspector - but that's something that I would like to avoid. Is there anything more automated?
I'm not a NodeJS expert, I just roughly understand what it is and how it works, so I'm pretty confident there could be something I missed that can be of help :)
What I'm thinking about right now is a batch script to find the required files, but that's all I have.
Just an additional note: code base is really old and big, and I do not really have permission to refactor existing code into es6 modules.
I found a solution: I'm going to create a testDebugLoader.js script in which I will write which test I want to debug, and an array of paths to scripts I need to load.
Then loop trough the array, load each needed file, and call eval on the retrieved text.

Using Jest with ReactDOM.findDOMNode

Jest (technically, Jest snapshot testing with ReactTestRenderer) does not work with ReactDOM.findDOMNode.
I've seen where I can work around this using Jest mocks. For example, if my ChannelScanner component uses my RadioButtonSet component, which wraps react-widgets' SelectList, then putting the following code in ChannelScanner.test.js allows snapshot testing to work:
jest.mock('../../src/components/forms/RadioButtonSet', () => 'RadioButtonSet');
However, this seems to break encapsulation: RadioButtonSet's tests have to set up mocks based on the components it uses, and the transitive components that they use, and this work has to be repeated for every component.
What's the best way to handle this? Short of enabling automock, is there a good way to say, "This module knows that it needs to mock itself whenever it appears?"

When using ES6 import statement, is there a way to protect against items being undefined?

import {
foobar1,
foobar2,
foobor3, //typo! this key doesn't exist in the module.
} from './module_file.js'
console.log(foobar1, foobar2, foobar3) //EXPLODES
One of the most frequent silly mistakes I make when using the new ES6 style import statement is that I'll have a typo in one of the keys in object destructuring. I can't think of a single instance where I'd ever want a value in a destructuring assignment to be undefined. Is there any way to force the import statement to fail-fast if one of the items I'm trying to import is undefined?
ie:
import {
doesntExistInModule //EXPLODE NOW! 🔥🔥🔥
} from './module_file.js'
There is no hook allowing a module to run some code before its dependencies load. This means that modules have no control over how
their dependencies are loaded.
There is no error recovery for import errors. An app may have hundreds of modules in it, and if anything fails to load or link,
nothing runs. You can’t import in a try/catch block. (The upside here
is that because the system is so static, webpack can detect those
errors for you at compile time.)
For more details, read it out
The module stuff in the spec is pretty gnarly, but I believe a real implementation will throw a SyntaxError at 15.2.1.16.4 ModuleDeclarationInstantiation( ) Concrete Method step 12.d.iii in that case. Since there are no legit implementations I don't know if you're talking about a way to do it in transpiled code in the meantime, or if you don't realize that's the case and will be satisfied to know it'll work that way eventually. There's been talk before of trying to implement that kind of check in Babel, but as far as I know nothing's actually been done to that effect. Babel compiles each module in isolation.
Also, this is not object destructuring, it just has similar syntax.

Categories

Resources