I am new to learning jest mocks. I am trying out a very simple test. I have a folder greeter. This folder has a file 'greeter.js' and a subfolder called 'test'. The 'test' folder has a test called greeter.test.js. Here is the code
greeter.js
function greet(fname, lname) {
return "hello " + greetWithName(fname, lname)
}
export function greetWithName(fname, lname) {
return fname + " : " + lname
}
export default greet
and the test is:
import greet, {greetWithName} from '../greeter'
jest.mock("../greeter")
describe('checks greeter module', () => {
it ('greet', () => {
greetWithName.mockReturnValue("hero");
expect(greet("a", "b")).toBe("hello hero") // fails
})
})
Curios why expect(greet("a", "b")).toBe("hello hero") fails, it is undefined. Not sure why ?
When checked for expect(greetWithName("a", "b")).toBe("hero") : passes !!
jest.mock('../greeter') is mocking the whole greeter.js module, and since you don't provide a mockReturnValue for greet it's returning undefined.
By default, jest.mock mocks all exports from a module to return undefined unless you provide mock return values. You're mocking greetWithName to return a value, but greet is also being mocked.
To get the behavior you want, you could either move greet into a separate module (which you don't mock), or use jest.requireActual to mock certain functions in a module but not others.
Related
When writing nodejs unit test case I want to mock variables.
let abc = awsMock.service.lambda;
In above code, awsMock I am creating and passing to method but service is json object which I want to mock. How I can pass some dummy value when above line is executed?
I want awsMock to be mock object only as I want to mock method using this after above code is executed.
I tried to stub variable but it didn't work
I recommend you encapsulate that clases who you need to mock in a service container and you can replace that values in your mock files.
I will use a minimal example case with three files:
- sc.js : The service container
- main.js: The main function
- test.js: The main function for tests
sc.js
// FAKE CLASS
class Lambda {
foo() {
return "real";
}
}
// END FAKE CLASS
// CODE START HERE
// The service container: could be an object, class, etc...
const serviceContainer = {
lambda: new Lambda(),
};
// Export SC
module.exports.sc = serviceContainer;
// Export properties with a wrap function (this is more easy to handle)
module.exports.getLambda = () => serviceContainer.lambda;
main.js
const { getLambda } = require("./sc");
module.exports.main = () => {
// Get lambda
const lambda = getLambda();
// Print foo()
console.log(lambda.foo());
}
test.js
const { sc } = require("./sc");
const { main } = require("./main");
// Exec before mock
main();
// Mock replace
sc.lambda = new class LambdaMock {
foo() {
return "mock";
}
}
// Exec after mock
main();
If you execute node test.js the output will be:
real
mock
So, you could use the getLambda() function in each file to get the Lambda Instance or the Mocked Lambda Instance.
NOTE: As a recommendation, you must wrap the AWS Lambda class in another class to make ensure the real and the mocked version will have the same functions.
I have an external library that is exported as a function, in the Stub documentation it only accepts an input with the first parameter as object and the second parameter as method , so how could I stub a library that is exported as a function in a Node ES Modules environment (without Commonjs)?
(In my specific case, I had used a library that use the internet to work, and I wanted to test derivated functions without accessing the internet, so I want to stub the external function library)
Attempts:
I couldn't use solutions like proxyquire as it is a solution based on require and module cache deletion, which are not supported within Node's ES modules.
I don't want to use proxyquire-universal because it simulates the operation of require in normal ES, and it's just a function in the whole project that I wanted to test, I was looking for a simpler solution
Changing the import mode doesn't work as it's not recompiled like in babel, so even if I import as import * as obj from 'lib' only the function name is changed
I had this error environment with 3 files:
An external library called "sum" for example, which I don't want to change, exported as follows:
External Library: externalSum.js
module.exports = function sum(a, b){
console.log(">>>>> running without stub <<<<<")
return a + b
}
This library used in the middle of a project file called mathProblems
Internal File: mathProblems.js
import sum from 'externalSum'
export function sumMore1(a) {
return sum(a, 1);
}
And I have a test file
Internal File: spec.js
import sinon from 'sinon'
import assert from 'assert'
import sumObj from 'externalSum'
import { sumMore1 } from '../mathProblems.js'
describe('sumMore1 is working', () => {
it('sumMore1 test', () => {
const sum_stub = sinon.stub(sumObj) // NOT STUBBING
sum_stub.withArgs(1, 1).returns(2) // NOT STUBBING
const result = sumMore1(1)
assert.strictEqual(result, 2)
});
});
I didn't find this solution anywhere on the internet, i found some solutions that work for node with request or babilon, but not for my case using ES Modules:
https://github.com/sinonjs/sinon/issues/562
https://minaluke.medium.com/how-to-stub-spy-a-default-exported-function-a2dc1b580a6b
So I wanted to register the solution in case anyone needs it.
To solve this, create a new file, which can be allocated anywhere in the project, in this case I will call it sumImport.js:
Internal File: sumImport.js
import sum from 'externalSum';
// export as object
export default {
sum
}
The object needs to be called inside the created function I want to test, and changed the import way:
Internal File: mathProblems.js
import sumObj from './sumImport.js';
export function sumMore1(a) {
const { sum } = sumObj;
return sum(a, 1);
}
And I finally managed to import as an object in the test:
Internal File: spec.js
import sinon from 'sinon'
import assert from 'assert'
import sumObj from '../sumImport.js'
import { sumMore1 } from '../mathProblems.js'
describe('sumMore1 is working', () => {
it('sumMore1 test', () => {
const sum_stub = sinon.stub(sumObj, "sum") // STUBBED
sum_stub.withArgs(1, 1).returns(2) // STUBBED
const result = sumMore1(1)
assert.strictEqual(result, 2)
});
});
I hope it helps someone and if someone else has some better solutions I would also be grateful!
I'm trying to write some tests in jest for an API that is intended to be shared with third parties. The inclusion method used by the third parties is a script tag which initiates the script but also passes along some data attributes via document.currentScript.
Here is a very cut down version of the code with the relevant parts:
// clientLib.js
export const getCurrentScript = () => document.currentScript;
export const initLib = () => {
const currentScript = getCurrentScript();
const reference = currentScript.getAttribute('data-reference');
const lang = currentScript.getAttribute('data-lang');
return new clientLib(reference, lang);
}
export class clientLib {
constructor(reference, lang) {
this._brand = reference;
this._market = lang;
}
}
window.clientLib = initLib();
// html
<script src="clientLib.js" data-reference="12345" data-lang="en-GB"></script>
What I'd like to be able to do in my test is something like this, but I've been unable to get anything to work:
// clientLib.test.js
import {
getCurrentScript,
initLib,
clientLib
} from './clientLib';
// here I want to mock the output of getCurrentScript() since document.currentScript does
// not exist, and I need the mock to return a script object with the two data attributes
// (ideally I need to be able to recreate this mock with both, either or none of the attributes)
// to test other cases
describe('initLib', () => {
it('returns a new instance of the library based on script attributes', () => {
window.clientLib = initLib();
// in here I should then be able to access properties on my lib on the window object
});
});
I did manage to get this working using an external setup file but for logistical reasons I don't think I can use this approach. I've tried to mock it in the test file itself and keep coming back to an "Invalid variable access: document" error. Is there a way to do this that I'm missing?
In many of my tests, I create a new slqite database and want to use the name of the test file as the sqlite DBname. This way, all my DB tests are isolated from each other.
As a related question: I would like my setupTestFrameworkScriptFile to be able to read config items (an Object) from the test. Is there a standard way to pass these configs from test to TestFramework?
Here is an example:
In myclass.test.js:
// "tell" the setup file I'm named 'myclass'
global.testId = function () {
return "myclass"
}
In setupTestFrameworkScriptFile:
beforeAll(async () => {
// if the test supports this method
if (typeof testId === 'undefined') {
return
}
// create an sqlite DB with that name
_createAndSyncDb(testId)
Instead I would like the code in setupTestFrameworkScriptFile to read the filename from the Jest environment, removing the need for the test and setupTestFrameworkScriptFile to both know about a previously agreed upon method named testId.
I've had trouble spying on exported function in a NodeJS (v9.6.1) app using Jasmine.
The app is written in typescript, transpiled with tsc in a dist folder to be executed as javascript.
App
I have a Foo utils file (foo.utils.ts) which exports functions:
import {readFile} from "fs";
export function getData(filePath: string){
const data = readFile(filePath)
// various checks happens here.
return data;
}
And a Bar class in a bar.ts file:
import {getData} from "../utils/foo.utils
export class Bar {
public do(){
// ... does stuff
const data = getData(filePath);
// etc.
}
}
Test
Now I'm trying to spyOn the exported getData method to check if it've been called and to return a mocked value. I don't want to read file in my unit test (and even to use the real getData method at all)
The bar.spec.ts file for testing:
describe("Bar", ()=>{
let bar: Bar;
beforeEach(function() {
bar = new Bar();
})
it(" should do stuff", ()=>{
const spy = spyOn(???, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
})
});
Problems
As it is a NodeJS app, I've been trying to use global as the object to spy on, but I get the error:
Error: : getAttachementData() method does not exist
I've also tried to add
import * as fooutils from ".../src/utils/foo.utils
And spy on fooutils but I still goes through the exported function (and crashes when it tries to read the file...)
Now I'm kinda lost. From what I've found it's not really possible to mock an exported function (even though they should be added to the global object).
I thought about refactoring the utils file to create an utils class which exports static method and spying on them.
Questions
Am I doing something wrong ?
Is it possible to spy (and replace) exported function ?
Would using static method in class (rather than export function) work ?
Any other way to that ?
You can wrap getData in an object { getData } and write your unit test as follows.
import { getData } from "../utils/foo.utils";
...
it(" should do stuff", () => {
const spy = spyOn({ getData }, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
});