Does anyone know if is it syntactically possible to apply the parameter properties as local scope methods for the parent function to reference.
The module is very large which increase page load times and shouldn't be imported via import() at top level.
import {method1, method2, method3} from "./module.js" //not an option
//Working example.
$('some-id').on('click', () => {
import ("./module.js")
.then( (module) => {
module.method();
module.method2();
module.method3();
// module methods...
});
}
//Something along these lines.
$('some-id').on('click', () => {
import ("./module.js")
.then( ( module ) => {
method();
method2();
method3();
//Apply the module methods to function scope without direct reference to the parameter
//this would save some time and loads of repetition if possible, question is can anything similar be done.
});
}
This would be pretty similar!
$('some-id').on('click', async () => {
const { method, method2, method3 } = await import("./module.js");
method();
method2();
method3();
});
Related
I am trying to mock the AWS SDK. I have a function, ingest that does some logic (including some calls to the AWS SDK sqs.deleteMessage function) and then calls ingestAndSave (which also makes some calls to the sqs.deleteMessage function).
When testing the ingest function, I want to mock my ingestAndSave function so I can have those tests focus purely on the ingest function logic.
Then I want to test the ingestAndSave function logic in its own describe block - and test the real thing, not the mocked version.
When testing either ingest or ingestAndSave functions I need to use my mocked sqs.deleteMessage
My most recent attempt includes following the path here: https://www.lucasamos.dev/articles/jestmocking. My test suite looks like this:
const ingest = require('./ingest')
const TEST_CONSTANTS = require('../../../tests/constants')
const mockdeleteMessage = jest.fn().mockImplementation( ()=> {
console.log("MOCK DELETE MESSAGE FUNCTION");
return {}
} )
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {}
}
},
SQS: jest.fn(() => {
return {
deleteMessage: jest.fn(( { params } )=> {
return {
promise: () => mockdeleteMessage()
}
}),
}
})
}
});
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
test("ingestAndSave will be called two times when event has two Records", async () => {
await ingest.ingest(TEST_CONSTANTS.sqsIngestEventMessageWithTwoRecords)
expect(ingestAndSaveFunctionSpy).toHaveBeenCalledTimes(2)
})
afterEach(() => {
jest.resetAllMocks()
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("FUNCTION: ingestAndSave", () => {
let validParams = {camera: TEST_CONSTANTS.validCameraObject, sourceQueueURL:"httpexamplecom", receiptHandle: "1234abcd"}
test("Will call deleteMessage once when given valid paramters", async () => {
await ingest.ingestAndSave(validParams)
expect(mockdeleteMessage.mock.calls.length).toBe(1)
})
/** snip other tests */
})
Both of the above functions run through code that looks just like this:
let deleteResp;
try {
deleteResp = await sqs.deleteMessage({ QueueUrl: sourceQueueURL, ReceiptHandle: receiptHandle}).promise()
} catch (err) {
console.error('Task: Deleting message from the ingest queue: ', err, 'deleteResp: ', deleteResp, 'receiptHandle: ', receiptHandle)
}
The mocked sqs.deleteMessage is used for the first describe block (i.e. I see the console.log for it) and the test passes.
However, the mocked sqs.deleteMessage function is not used for the second describe block (i.e. I do not see the console.log message indicating the mocked function ran, and, in fact, I get a 5000ms timeout, indicating the real sqs.deleteMessage was called (with invalid authorization, the deleteMessage command takes >5 seconds).
I thought the jest.restoreAllMocks() in the afterAll block of the first describe is restoring my mock. So I go with explicitly restoring the ingestAndSaveFunctionSpy mock with ingestAndSaveFunctionSpy.mockRestore() instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the first describe block, but it has been restored by the ingestAndSaveFunctionSpy.mockRestore() call.
Just to test, I remove the afterAll in the first describe entirely. This results in the second test calling the mocked implementation of ingestAndSave and thus the test failing.
Declare the jest.mock... within each describe block, but this isn't allowed due to jest.mock calls getting hoisted to the top of the file.
How can I mock a module using jest.mock(... and have it persist between describe blocks while allowing mockRestore() calls to other mocked functions?
How to change mock implementation on a per single test basis? has me looking at mockImplemention but I'm struggling to see how I'd implement it.
See related question attempting to tackle this from a different angle: How to have jest.mock persist between tests?
If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
// ...
afterEach(() => {
ingestAndSaveFunctionSpy.mockRestore()
})
})
In the outer scope, I would also advise using a:
afterEach(() => {
jest.clearAllMocks()
})
This will reset the recordings of the mock calls to your aws-sdk mock without actually changing any of the mocked behavior. Otherwise, you might be verifying behavior in one test that actually happened during the execution of another.
Without seeing the code, it's hard to offer more specific advice, but this style of test is a code smell, IMO. Any time you have to spy on your own unit in a unit test, it's usually a sign of poor organization. Try to arrange it so that discreet units of work are discreet functions. As an abstract example, instead of chaining functions together like:
const a = (arg) => {
const stuff = /* do some stuff with arg */
b(stuff);
};
const b = (arg) => {
const stuff = /* do some stuff with arg */
c(stuff);
}
const c = (arg) => {
/* do some stuff with arg */
}
Instead make them discreet, testable units that are joined together in the external function:
const abc = (arg) => {
const firstStuff = a(arg);
const secondStuff = b(firstStuff);
c(secondStuff);
}
const a = (arg) => {
return /* do some stuff with arg */
}
const b = (arg) => {
return /* do some stuff with arg */
}
const c = (arg) => {
/* do some stuff with arg */
}
Now you can write tests for a, b, and c independently without having to mock your own internals.
I solved this by wrapping all calls to aws-skd functions in my own functions and then mocking my wrapper functions.
So now I have a utility functions in utils.js that looks like this:
module.exports.sendMessage = async (params) => {
return await sqs.sendMessage(params).promise()
}
And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):
await utils.sendMessage(sendMessageParams).catch(err => throw (err))
And then during testing, I mock it like this - either in a beforeEach or directly before my test:
sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true}))
Later, rinse, and repeat for all aws-sdk functions I want to mock.
I am currently working on a project where I want to deference an array of functions (function references) and excecute the function.
This does only work, if I don't call another class method within the function.
Otherwise I get "Uncaught TypeError" and I can't figure out how to solve this error.
Here's my code sample 'working' the same way my original project does:
After calling function2 the engine cannot find this.log...
Do you have ideas? Thank you very much in advance.
KR, Robert
class ArrayWithFunctions {
constructor() {
this.functionTable = [
this.function1,
this.function2,
];
}
execute(index) {
return (this.functionTable[index])();
}
log(chars) {
console.log(chars);
}
function1() {
console.log('I am Function 1.');
}
function2() {
this.log('I am Function 2.');
}
}
let example = new ArrayWithFunctions();
example.execute(0);
example.execute(1);
This is an example of Javascript's execution contexts in action. In this situation, to avoid losing the correct reference to the class, you can bind the functions when putting them inside the array, or initialize them as arrow functions:
Example 1: Bind them in the constructor:
constructor() {
this.functionTable = [
this.function1.bind(this),
this.function2.bind(this),
];
}
Example 2: Create them as arrow functions:
class ArrayWithFunctions {
// ...
function1 = () => {
console.log('I am Function 1.');
}
function2 = () => {
this.log('I am Function 2.');
}
}
You can use arrow functions to dodge scoping issues:
function2 = () => {
this.log('I am function 2.');
}
Related: How to access the correct `this` inside a callback (and you might also want to take a look at How does the "this" keyword work?).
In this case you can simply set the correct this value by calling the function with .call:
return this.functionTable[index].call(this);
I wanted to mock out a function and make sure it has executed a certain number of times.
The tricky part is that the function I wanted to mock out is part of the returned result of another function
My implementation is roughly like
const theFnIWantedToMock = jest.fn()
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
return {
...actualHooks,
someHooks() {
return
{
theFnIWantedToMock,
}
}
}
})
describe('test', () => {
it('some test', () => {
//...
expect(theFnIWantedToMock).toHaveBeenCalledTimes(1)
})
})
but Jest is throwing an error saying that Invalid variable access theHookINeedToMock. Does anyone know what is the correct way of doing it?
This problem is described in the documentation:,
A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'. It's up to you to guarantee that they will be initialized on time! For example, the following will throw an out-of-scope error due to the use of 'fake' instead of 'mock' in the variable declaration
Prefixing a variable with mock disables Jest check. let or const variables are in temporal dead zone before declaration, that they are accessed when a factory is evaluated results in runtime error in untranspiled ES6. For eagerly evaluated mocked modules a mock needs to be defined inside a factory.
A way to avoid this is to use var declaration that is hoisted and initialize it inside a factory:
var theFnIWantedToMock
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
theFnIWantedToMock = jest.fn()
return {
...actualHooks,
someHooks: jest.fn().mockReturnValue(theFnIWantedToMock),
}
})
A way to keep a reference to it is to keep it a part of the import:
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
const theFnIWantedToMock = jest.fn()
return {
...actualHooks,
theFnIWantedToMock,
someHooks: jest.fn().mockReturnValue(theFnIWantedToMock),
}
})
This makes theFnIWantedToMock available as a part of import object, also works for reusable mocks in __mocks__.
I'm relatively new to the latest and greatest of Javascript (as a holdover from my IE6 days) and I dont fully understand the syntax. I am writing a Wordpress theme using Sage's Roots. The way they set up the JS is that each page gets a boilerplate JS file
home.js
export default {
init() {
},
finalize() {
},
}
Where init is called on page load and finalize on unload. I'm trying to break up my init into functions but I cant figure out the scoping issues.
export default {
init() {
let trigger = document.body;
trigger.addEventListener('click', function() {
// how do i call 'something'?
});
},
finalize() {
},
something() {
console.log('something happened');
}
}
Normally, I might create a variable in a higher scope and save this upon init, e.g., var klass = this and reference it using klass.something() but I cant figure out where to even put that line.
How do I reference the something method when this has been overwritten in a different scope?
EDIT: Also noteworthy: I want to avoid polluting the global namespace.
As you're exporting an object, you can an arrow function. Arrow functions don't define their own this and so you can use the this of the object.
export default {
init: () => {
let trigger = document.body;
trigger.addEventListener("click", () => {
this.something();
});
},
finalize: () => {},
something: () => {
console.log("something happened");
}
};
Arrow function in Javascript are lexically scoped, they define 'this' based on where they are written.
Instead going for arrow function go with regular function declaration which is 'dynamically scoped' and here you will be able to call 'something()' using 'this'
Change you code like below -
export default {
init() {
let trigger = document.body;
trigger.addEventListener('click', function () {
this.something();
});
},
finalize() {
},
something() {
console.log('something happened');
}
}
I use VueCLI and i do have such code inside methods:
submitCheck: function () {
function authUser() {
// returns a promise
}
function uploadFile() {
// also returns a promise
}
// ...
if ( error !== null ) {
EventBus.$emit('showError', error)
} else {
authUser()
.then(
function () {
return uploadFile();
})
.then(
function (data) {
EventBus.$emit('loaderStop')
this.$router.push('/awaiting');
})
.catch(function(error) {
EventBus.$emit('loaderStop')
console.log(error)
})
}
What i want to achieve is to route to /awaiting if all promises are resolved, but since i use this inside an anonymous function it doesnt have router. I am sure many coders met such a problem and needed to route from inside a function. How to do it?
Kalreg.
Multiple ways to handle this, I'd suggest you use arrow functions and learn about their differences to the other function style.
to be clear:
replace
function (data) {
EventBus.$emit('loaderStop')
this.$router.push('/awaiting');
}
with
data => {
EventBus.$emit('loaderStop');
this.$router.push('/awaiting');
}
You question context is not clear enough. If the code is executed in exponent methods, you can use arrow funciton like (agrs,...) => {...}. Otherwise, if this is not the case, you can use bind function like (function() {}).bind(this). Or just import $router in your code module.
Hope this can help you.
The answers from Sandro and Xhua are perfect. I just want to explain, WHY you get the error:
The problem is "this.". It refers to the parent object. So in your case "this." refers to the authUser Object and not to Vue. For your understanding: You could define "var that = this" outside of your authUser object and then use "that." inside. Or you go for the more sophisticated solutions.