I want to test my React app and mock the backend calls. I decided to replace fetch by a Jest function that returns a constant.
The problem is that I can't overwrite the default fetch value. I googled a bunch and found global.fetch = ... to overwrite fetch but I'm not sure what global means. I tried just writing var fetch = ... in my test file but did not work although the component is within the scope of component.
I'm happy to hear alternative solutions for mocking fetch.
// Does not work
import component that fetches
test(...){
var fetch = ...
<component that fetches/>
}
// Works
import component that fetches
test(...){
global.fetch = ...
<component that fetches/>
}
It's expected that the first option doesn't work because fetch variable is local to a function where it was defined. although the component is within the scope of component statement doesn't make much sense, that a component is nested (or more specifically, React element, because <Comp/> translates to React.createElement(Comp)) doesn't mean it can access anything from that scope except variables that were specifically passed as props.
This works like:
function foo() {
var fetch = 'local';
var someGlobal = 'local';
console.log(fetch); // a global shadowed by a local
console.log(someGlobal); // a global shadowed by a local
bar(someGlobal);
}
function bar(someGlobal) {
console.log(fetch); // a global
console.log(someGlobal); // a global shadowed by a local
}
Since real requests aren't supposed to be performed in tests, it's acceptable to mock fetch by assigning it to a global like global.fetch = ..., but for other globals this would it impossible to restore original implementation. Generally, Jest spies should never be set by assignment. Instead, spyOn is used:
beforeEach(() => {
jest.spyOn(global, 'fetch').mockImplementation(...)
});
This allows the framework to restore original implementation if needed, this spy works correctly with both resetAllMocks and restoreAllMocks.
Related
The situation
In my Vue app, I have a Vue component which mounts an svg, which I have defined with a few props. These are a combination of reactive and non-reactive data.
The reactive data that we need is percData, which therefore sits in the data(){} object.
We also have colours, width, height, and scale, which are not reactive and never will be. I don't call these in the <template> block, and I don't plan for them to change. These are currently declared with const, and are not within the export defautl{} block scope.
The question(s)
Where is the best place to be for these const declarations?
What scope are these const-declared variables currently in?
More generally, how does scope work for the <script> tag in Vue in a multi-component app? Is each script/component a separate scope from the global one? And where is the global scope?
My possible understanding
As per this thread and this thread, I think the best place for my const would be in a separate file, from which I would then import them in my mySvgComponent component. Is this the correct approach?
My code
<template>
<div></div>
</template>
<script>
import { mySvgComponent} from '../mySvgComponent'
import { select } from 'd3'
const [colour1, colour2, colour3, colour4] = ['#000000', '#111111', '#222222', '#3333'];
const width = 135
const height = 135
const scale = .75
export default {
name:'mySvgComponent',
data(){
return{
percData: null
}
},
props: {
summary: Object
},
methods: {
percSetup(summary) {
return this.percData = [
{ colour: colour1, perc: +summary.colour1Percentage.toFixed(2)},
{ colour: colour2, perc: +summary.colour2Percentage.toFixed(2)},
{ colour: colour3, perc: +summary.colour3Percentage.toFixed(2)},
{ colour: colour4, perc: +summary.colour4Percentage.toFixed(2)}
]
}
},
async mounted() {
this.percSetup(this.$props.summary)
const svg =
select('div')
.append('svg')
.call(mySvgComponent(this.percData)
.width(width)
.height(height)
.scale(scale))
}
}
</script>
<style></style>
Related threads and why I don't think they answer my question:
How to set a component non-reactive data in Vue 2?, How to make a template variable non-reactive in Vue, How could I use const in vue template?. I don't call my const variables in my <template> tag, and I don't need it to be responsive.
What is the best way to create a constant, that can be accessible from entire application in VueJs ?. Maybe I don't understand this fully. Why would I need to run a method() to return my const variables?
Vue SFC is syntax sugar, it's necessary to understand what code it's compiled to in order to use it effectively.
The result of SFC script syntax is ES module with roughly the same content as the body of <script> block, with name and render options being added by the compiler. Then general modular JavaScript practices are applicable. The constants can remain in current module if they don't take much lines and aren't reused in other modules, and can be moved to a separate module otherwise. In case the constants are supposed to be used in a template, they can be returned from data or setup function.
The result of SFC script setup syntax is ES module with the whole block being moved to generated setup function, with the exception of imports. In this case it's inefficient to declare constants in this block because they will be created on each component instantiation, this may be a reason to move them to a separate module, although this can be considered preliminary optimization.
Considering the above, the scope of Vue SFC modules works exactly like it's expected from ESM because this is what they are compiled to.
Vue composition API provides markRaw function to additionally prevent constant objects from being made reactive when they are used inside reactive ones like data, in case this is unwanted. The same is done for options API with Object.freeze in linked question.
TL;DR: the code in the question is ok, it's correct to use the constants like that in this case.
In Vue.js, you can set non-reactive (i.e., constant) variables by declaring them in the data object as a function that returns an object, rather than declaring them directly as properties of the data object. This ensures that the variables are only set once during the initialisation of the component and cannot be modified later. Here's an example:
data: function () {
return {
nonReactiveConst: 'This is a non-reactive const variable'
}
}
Another way to create a non-reactive variable in Vue is to use the Vue.observable() function to create an observable object, and then assign the non-reactive variable as a property of that object.
const state = Vue.observable({
nonReactiveConst: 'This is a non-reactive const variable'
});
You can then access the non-reactive variable inside the component's template using state.nonReactiveConst.
It's important to note that using this method, you can still mutate properties of the object, but you can't reassign the whole object.
I want to create a helper that generates some data and saves it in some variable and on the next execution, it should use the memoized value for calculation.
Basically it's a helper for the High Order wrapper. It means that the storage should be created for every HOC but it shouldn't be re-created on the next re-render.
Now it looks like:
pseudo code
var storage; // I want to create this storage for every HOC only once.
function createDynamicStyles(theme, stylesCreator, status) {
// create a styles registry only once. This function can be re-called by the same HOC
// on each re-render so I want to use the memoized registry.
if (!storage) {
storage = stylesCreator(theme);
};
return storage[status];
}
const styleCreator = theme => ({
disabled: { color: theme.disabled },
success: { color: theme.success }
})
const Component_1 = componentHOC((props) => {
const { theme, status } = props;
// I'd like to keep this helper call as simple as possible. It already has 3 arguments.
const finalStyle = createDynamicStyles(theme, stylesCreator, status);
})(AwesomeComponent)
// these props can be changed during runtime
<Component_1 disabled={false} success={true} />
The functionality flow of this helper can be divided into 2 steps.
1) The first HOC call. It creates the styles based on the theme and saves them in the storage
2) Next Re-render of the HOC. It should fetch the previously created styles and return memoized value. This value should be unique for each HOC.
The problem is that the same helper can be used for other Components as well and it means that we can't use the same storage because it will be overwritten but the 'latest' HOC.
The possible ways how to solve it:
1) Create a class that will contain storage itself and creates a new Instance for each HOC.
To be honest, I'd like to avoid it because it looks too complicated for me in this case.
2) Create some Shared Registry and pass the UUID for every HOC.
It'd be nice but I don't know how to automatically do it. I don't want to manually pass the UUID on each HOC. I'd like to have this functionality under the hood to keep HOC calls, lightweight.
I was thinking about the new Map, and saving the created styles as Key-Value pair but it simply doesn't work as we don't have the generated KEY reference in the HOC. So we can't use it as a key.
Is it possible to do such a thing in the case of plain functions only?
Maybe I missed some other interesting variants.
Thanks for any help and suggestion.
Kind Regards.
I am new to JavaScript testing and currently trying to write some test cases for a store (just an ES6 class) I created. I am using Jest as this is what we usually use for React projects, although here I am not testing a React Component but just a class wrapping a functionality.
The class I am testing extends another class, and has various methods defined in it. I want to test these methods (whether they are called or not), and also whether the properties declared in the class change as and when the corresponding class methods are called.
Now I have read about mocking functions, but from what I understand, they can only do checks like how many times a function is called, but can't replicate the functionality. But in my case, I need the functionality of the methods because I will be checking the class member values these methods change when called.
I am not sure if this is the right approach. Is it wrong to test functions in Jest without mocking? And inferentially, to test the internal workings of functions? When do we mock functions while testing?
The issue I am facing is that the project I am working on is a large one where there are multiple levels of dependencies of classes/functions, and it becomes difficult to test it through Jest as it will need to go through all of them. As I am using alias for file paths in the project, Jest throws errors if it doesn't find any module. I know its possible to use Webpack with Jest, but many of the dependent classes/functions in the code are not in React, and their alias file paths are not maintained by Webpack.
import { getData } from 'service/common/getData';
class Wrapper extends baseClass {
someVariable = false;
payload = null;
changeVariable() {
this.someVariable = true;
}
async getData() {
super.start();
response = await fetchData();
this.payload = response;
super.end();
}
}
This is a small representation of the actual code I have. Can't post the entire class here as I am working on a remote machine. Basically, I want to test whether changeVariable gets called when invoked, and whether it successfully changes someVariable to true when called; and similarly, check the value of payload after network request is complete. Note that fetchData is defined in some other file, but is critical to testing getData method. Also the path used here (service/common/getData) for importing getData is not the absolute path but an alias NOT defined in Webpack, but somewhere else. Jest can't resolve getData because of this. I will not have to worry about this if I mock getData, but then I will not be able to test its functionality I believe.
#maverick It's perfectly okay to test your class methods using jest. Check the code example in the link -
https://repl.it/repls/ClumsyCumbersomeAdware
index.js
class Wrapper {
constructor(){
this.someVariable = false;
}
changeVariable(){
this.someVariable = true;
}
getData(){
return new Promise(resolve => resolve('some data'));
}
}
module.exports = Wrapper;
index.test.js
const Wrapper = require('./index');
const wrapper = new Wrapper();
describe('Wrapper tests', () => {
it('should changeVariable', () => {
wrapper.changeVariable();
expect(wrapper.someVariable).toBe(true);
});
it('should get some data', () => {
wrapper.getData().then( res => expect(res).toBe('some data'));
});
});
This is a very simplistic example and in real life the async calls are much more complicated and dependent of 3rd party libraries or other project modules. In such cases it makes sense to have all the dependencies injected in out class and then mocked individually. For Example -
class GMapService {
constructor(placesApi, directionApi){
this.placesApi = placesApi;
this.directionApi = directionApi;
}
getPlaceDetails(){
this.placesApi.getDetails('NYC');
}
getDirections(){
this.directionApi.getDirections('A', 'B');
}
}
Now you can easily mock placesApi and directionApi, and test them individually without actually requiring Google Map dependencies.
Hope this helps ! 😇
Problem
I want to have a global counter in react native to show how many notifications a user has in an app that I am creating.
I created a global variabal in a file named global.js:
var notifNum = 0;
global.n = notifNum;
then in my code, I import it with
import './global.js'
then later I try to update it with
global.n = notifNum;
Notif num being the number of notifications a user has, defined in a function. The problem is that global.n stays as 0, and doesn't change at all.
How can I edit the global.n variable?
There is one hacky way to modify a global.foo variable.
Dosnt work
global.foo = 'foo'; //this cant be changed
global.foo = 'foofoo';
console.log(global.foo); //foo
Does work
global.foo = ['foo']; //this can be changed
global.foo[0] = 'foofoo';
console.log(global.foo[0]); //foofoo
This has less to do with React-Native and more to do with how javascript works as a language.
In short, you must export what you wish to use in other files.
Global.js
let counter = 0;
const increment = () => {
counter++;
console.log('counter_updated', counter);
};
const worker = {
increment,
counter
};
module.exports = worker;
We've told the JS engine exactly what we want to import in other files. Assigning it to and object and exporting the object is not necessary but allows for cleaner imports/exports IMO.
Component.js
import { counter, increment } from 'pathTo/global';
We deconstruct the object we exported and are now able to modify/use values in the global.js file.
Food for thought
This is not an ideal solution as it is not two ways. Incrementing the count in the global.js file will not update the value in the component.js.
This is because React needs to have it's state updated to initiate a re-render of a component.
Also, this will not persist through app reloads, which I believe you would want for something such as notifications.
Other solutions
Storing the notification counts in a database.
Using component state to manage the count.
Using a state manager like Redux or MobX
I'm primarily a C# developer with limited experience in JavaScript/EcmaScript and trying to understand the right way to create an instance of an API service that I can reuse in my functions.
The app I'm working on is a React app and the API service I'm trying to consume is Google Places. I created an external file for all Google Places related functions so that they're reusable. Here's the function I have in that file that will make the API call to get some suggestions from Google.
export const googleCall = (keyword) => {
const googlePlacesAutocomplete = new google.maps.places.AutocompleteService();
googlePlacesAutocomplete.getQueryPredictions({input: keyword}, callback);
}
It makes no sense to keep "new"ing google.maps.places.AutocompleteService() every time a new keystroke comes into this function.
One option I can think of is that in my React app -- say in ComponentWillMount --- I can create an instance of this service and then pass it into this function along with the keyword but that seems a bit cumbersome. That would look something like this:
export const googleCall = (googleService, keyword) => {
googleService.getQueryPredictions({input: keyword}, callback);
}
I think a better way would be to make sure the googleCall() function can access what's in the state i.e. in my redux store. In other words, I could create an instance of the googleService in a ComponentWillMount function and place it in my redux store. Just not sure how I can have this function access what's in my state.
Or, more generally speaking, in ES -- in particular in ES2015 -- how do I create an instance of this service that my googleCall function can access every time I call it without having to create a new instance of the service? I'm just not sure how this is done in ES where we create an instance of something and keep it in memory for subsequent calls.
UPDATE:
In my React component, I simply get the googleCall function like this:
import {googleCall} from '../googlePlacesFunctions';
I'm somewhat confused by your question. Why not just?
// api.js
const googlePlacesAutocomplete = new google.maps.places.AutocompleteService();
export const googleCall = (keyword, callback) => {
googlePlacesAutocomplete.getQueryPredictions({input: keyword}, callback);
}
That way you're not passing things around anywhere and you're not creating more than one instance of AutocompleteService