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
Related
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.
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 need to mock API calls for a button click but the actual call is nested down in a utility file that is called by a middleware file. Some framework code was using Jest, axios-mock-adapter, and Enzyme. (I'm still wrapping my head around what each of these do).
So let me preface this. I'm an intern at a company where my task is to test some JS code for a piece of software built on a microservice architecture. So first let me apologize for any improper verbage. My background is in C/C++ and x86 assembly. No, I didn't fudge my resume when applying for this position. The company was fully aware that I had little to no experience with JS. I've attempted to create a mock = MockAdapter('axios') then calling that with mock.OnGet().reply() but when checking my coverage it seems to error every time.
Theres to much code to post so I'll try to give an example
class ComponentName extends component {
stuff
}
ComponentNameFunc {
this.middleware.funcName.then(
response ()=>{}
errorRespone ()={}
)
}
//funcName is a name of a middleware function that calls a function
//in the utility file. The utility file does the axios.get call
When I render the component then simulate a button click it calls this.middleware.funcName but then the coverage shows it going to the errorResponse portion. Heres a test example
describe('test',()=>{
test('button click', done => {
mock.onGet('aURL').reply(200,mockData);
Enzyme.configure({ adapter: new Adapter() });
const wrapper = shallow(
<ComponentName/>);
expect(wrapper.exists()).toBe(true);
wrapper
.find("Button")
.at(0)
.simulate("click");
done();
)};
)};
EDIT: So I found part of the issue. I had multiple mocks for different API calls and apparently only 1 was registering. However, some of these functions that I'm testing will make two API calls. How do I mock up two separate API calls for a single test? Originally I had some thing like this
import axios from "axios"
let mock = MockAdapter(axios);
let mock2 = MockAdapter(axios);
mock.OnGet("URL").reply(200,Data);
mock2.OnGet("URL2").reply(200,DifferentData);
So I figured it out. I was trying to make multiple mock variables (or are they objects?) like mock, mock2, mock3. It seems replicating mock.OnGet.reply with different information works just fine.
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 ! 😇
Based on the scaffolder mern.io I was going through the code to see what was going on. I stumbled upon a .need method which looks like something related to es6 classes. I can't seem to find any usable info anywhere, so I ask what is the .need method?
class PostContainer extends Component {
//do class setup stuff here
}
PostContainer.need = [() => { return Actions.fetchPosts(); }];
You can get the project up and running very easily with these commands.
npm install -g mern-cli
mern YourAppName
The mern documentation is pretty terse when it comes to explaining this.
fetchComponentData collects all the needs (need is an array of actions that are required to be dispatched before rendering the component) of components in the current route. It returns a promise when all the required actions are dispatched.
Reading through the code is a much clearer way of finding out what's going on here.
Overview
It's a way to specify some actions that should be dispatched before rendering the component.
This component maps the posts property from the Redux store to a prop called posts so that it can render the list of posts.
// PostContainer.jsx
function mapStateToProps(store) {
return {
posts: store.posts,
};
}
However, initially this property will be empty because the posts need to be fetched from an asynchronous API.
// reducer.js
// initial state of the store is an empty array
const initialState = { posts: [], selectedPost: null };
This component needs the posts to be available before it renders, so it dispatches the action returned from the call to Actions.fetchPosts().
// actions.js
export function fetchPosts() {
return (dispatch) => {
return fetch(`${baseURL}/api/getPosts`).
then((response) => response.json()).
then((response) => dispatch(addPosts(response.posts)));
};
}
When the action has finished dispatching, the store's data can be mapped to the connected component.
Caveat
This isn't a universal way to specify asynchronous dependencies for React components. It only works because mern has a utility method called fetchComponentData that it calls at the server side, in order to populate the Redux store before rendering.
// server.js
fetchComponentData(store.dispatch, renderProps.components, renderProps.params)
This method traverses the components from the second argument to extract the needs from each. Then it executes 'needs` and waits for all the promises to complete.
// fetchData.js
const promises = needs.map(need => dispatch(need(params)));
return Promise.all(promises);
When the promise returned by Promise.all(promise) completes, the Redux store will be populated and the components can safely render their data to be served to the client.
Syntax
You mentioned that you thought it might be related to ES6 classes, so I'll cover the syntax quickly too.
ES6 classes can't have static properties specified in the class literal, instead we have to declare them as properties on the class after it has been defined.
The needs property must be an array of functions that return promises to work with fetchComponentData. In this case we have an arrow function declared inside an array literal. It might help to look at it split up into separate variables.
const fetchPosts = () => { return Actions.fetchPosts() };
const needs = [fetchPosts];
PostContainer.need = needs;