Function used for assigning something as an object's property? - javascript

Came across this piece of code recently:
const fetchMock = async (name, type="success") => {
const response = await fetch(someUrl);
let mock = await response.json();
mock = mock[type];
mock.json = () => mock.data;
return mock;
}
I have trouble understanding why, on line 5, we use a function to assign the mock.data to mock.json. Can't we simply write mock.json = mock.data? How is it different?
P. S. I don't have any idea about the kind of data it is receiving.

Your question has nothing to do with the async JSON fetching stuff. In the end, mock is just an object which has a data property. It also needs a json property that yields the data property.
So a reduced code sample would look like this:
const mock = {
data: {
"someKey": "someValue"
}
};
mock.json = () => mock.data;
Assume that mock.json is set exactly once, and mock.data is mutated or updated. Then, mock.json = mock.data will only work correctly if mock.data is an object that stays the same reference.
const mock = {
data: {
"someKey": "someValue"
}
};
mock.json = mock.data;
console.log(mock.json); // { "someKey": "someValue" }, as expected
// …
mock.data.someKey = "someOtherValue"
console.log(mock.json); // { "someKey": "someOtherValue" }, as expected
// Either of these reassignments will break up the connection between `mock.json` and `mock.data`:
mock.data = {"newKey": "something"};
mock.data = "Something else";
console.log(mock.json); // { "someKey": "someOtherValue" }, no longer updated
This doesn’t matter for mock.json = () => mock.data, though, as the function just returns the current value of mock.data.

Related

Sinon stub - mocking a function which returns an array of objects

I am trying to stub the following code
async function logUpdate(client) {
const results = await client.query(query.toParam());
const { count, timestamp } = results.rows[0];
await db.updateDatasourceLogs(destdb, DB.src, TABLES.src, timestamp, count);
}
This is the following code i am using to stub the above code
fakeClient = {
query: sinon.stub().resolves(fakeRows),
};
const rowData = {
count: 1,
timestamp: ''
};
fakeRows = {
rows: sinon.stub().returns([rowData]),
};
fakeSequel = {
useFlavour: sinon.stub().returns(toParam: () => false,),
};
I am getting an error for destructuring
TypeError: Cannot destructure property count of 'undefined' or 'null'.
at line
const { count, timestamp } = results.rows[0];
how to stub the above line?
If we look at the body of your logUpdate function, we see it begins with the following two lines:
const results = await client.query(query.toParam());
const { count, timestamp } = results.rows[0];
This code says:
Await the Promise returned by the call to client.query and assign
it to a variable called results.
results is an Object with a property called rows which is an
Array whose 0th element should be an Object with count and
timestamp properties - which we destructure into local variables.
This implies that the value of results looks like:
{
"rows": [
{
"count": 1
"timestamp": "0"
}
]
}
However, in our stubbing code, we have the following:
fakeRows = {
rows: sinon.stub().returns([rowData]),
};
Which says that fakeRows is an Object with a rows property whose value is a function that returns [rowData].
If we were to implement this Object without sinon, it would look like:
{
"rows": function () {
return [
{
"count": 1
"timestamp": "0"
}
];
}
}
Notice the difference between the structure that logUpdate is expecting and what fakeRows actually provides. Specifically, logUpdate expects results to have a rows Array, but, with our stubbed code, it has a rows Function!
We can fix this simply by having fakeRows reference our rowData directly:
const fakeRows = {
rows: [rowData]
};

How to make a promise object immutable when setting different states in Javascript?

I'm working with React/Redux and want to use the same Promise result to set two different states. Here's my code:
useEffect(() => {
fetchData().then((resp) => {
Object.freeze(resp);
const var1 = resp[0];
const var2 = parseData(resp[0]);
props.setAction1(var1);
props.setAction2(var2);
});
}, []);
The content of resp is an array of dicts:
Array [ (95) […] ]
0: Array(95) [ {…}, {…}, {…}, … ]
​
0: Object { date: "6/1/11", open: 21.45673929 }
​​
1: Object { date: "6/2/11", open: 21.02743338 }
​​
2: Object { date: "6/3/11", open: 20.64964196 }
​​
etc...
I know that in Javascript object are mutable, so I attempted to make resp immutable with Object.freeze(resp). var1 should be the result with string dates and var2 should be the result with date converted to Date objects with the parseData function, whose content is:
function parseData(data) {
data.map((x) => (x.date = new Date(x.date)));
return data;
}
My problem is that, when I run this function, var1 === var2. The parseData function affected not only var2 but also var1. I don't understand how this happens, especially as I froze the resp object. This is likely an issue with immutability, but I'm unsure where else to go.
Your parseData function currently returns the same array object, in which the objects were mutated. If you want your objects to be immutable, you should not have code like x.date =. Instead do this:
function parseData(data) {
return data.map((x) => ({...x, {date: new Date(x.date)} }) );
}
I don't think Object.freeze is what you're looking for. In the end, the objects still reference the same location in memory, even if they are frozen. You should make a copy of the data to separate the two so you can alter either of them without affecting the other one.
fetchData().then((resp) => {
const ref1 = resp;
/* Alternatively you can also use `Object.assign` */
const ref2 = [ ...resp ].map(obj => ({ ...obj }));
}
Like #JohnD mentioned, you can also do that in your parseData function which might be a little tidier. ;)
I don't believe Object.freeze(resp); will freeze nested objects. You could try the following, but I believe it is confusing to read.
useEffect(() => {
fetchData().then((resp) => {
Object.freeze(resp);
Object.freeze(resp[0]);
const var1 = resp[0];
const var2 = parseData(resp[0]);
props.setAction1(var1);
props.setAction2(var2);
});
}, []);
Alternatively, you could change your parseData function to return a copy using the spread operator:
function parseData(data) {
[...data].map((x) => (x.date = new Date(x.date)));
return data;
}
Object.freeze will freeze the direct properties of the object you pass. It will not do this recursively, so giving it an array or nested object will not do what you expect.
The parseData function affected not only var2 but also var1.
No. Here, var1 === var2 because your parseData code does not modify the object at all, but only a single property of it. It's the same object instance all-along. When you set x.date = new Date(x.date), the x itself does not change.
Not sure if that's going to be necessary, but you could freeze each individual object in resp, using map().
const freezeAll = items => items.map(item => Object.freeze(item));
const parseData = items => items.forEach(item => item.date = new Date(item.date));
// fetch data, process, and cache it
const data = fetchData().then(parseData).then(freezeAll);
// use data
useEffect(() => {
data.then(items => {
props.setAction1(items[0]);
props.setAction2(items[0]);
});
}, []);

Javascript: How to mock unknown object structure properties

I'm trying to create a dynamic mock for a javascript object.
I have a react component that receives an object with a dynamic structure (meaning i don't know which properties exist on it beforehand) and each property is either a json with any level of nesting or a function that returns a string.
example object:
const originalObj = {
prop1: {
innerProp2: {
moreProps: () => "some string",
differentProp: () => "some string 2",
anotherProp: () => "something else",
},
funcAtLevel2: () => "jlk"
},
funcAtThisLevel: () => "this returns string too"
}
The component renders the strings that it receives from this object and i'm testing other functionality of the component.
in order for the component to render i need the accessors to work, so if my component looks like this:
function MyComp(props: { content: any }) {
const { content } = props;
const moreProps = content.prop1.innerProp2.moreProps(); // moreProps = "some hard code string"
const differentProps = content.prop1.innerProp2.differentProps(); // differentProps = "some hard code string"
const anotherProp = content.prop1.innerProp2.anotherProp(); // anotherProp = "some hard code string"
const funcAtLevel2 = content.prop1.funcAtLevel2(); // funcAtLevel2 = "some hard code string"
const funcAtThisLevel = content.funcAtThisLevel(); // funcAtThisLevel = "some hard code string"
// other code...
return (
<div>
<span>{moreProps}</span>
<span>{differentProps}</span>
{/* more DOM structure here */}
</div>
);
}
I need the accessors to work dynamically.
I managed to get closer with this:
const handler = {
get(target, prop, receiver) {
return new Proxy({ [prop]: () => "some hard code string" }, handler);
},
};
const CompToRender = <MyComp content={new Proxy({}, handler)} />;
However I couldn't manage to return the last accessed property as method. How can I implement it?
EDIT: what i'm looking for is the magical object that i can pass to the rendered component instead of the one that is passed in production which will return some hard coded string so the component won't fail to render (the string value is of none importance).
const magicalObj = // how to define this without defining each property explicitely? the "content" object can be very large (over 1000 keys)
const CompToRender = <MyComp content={magicalObj} />;
function MyComp(props: { content: any }) {
const { content } = props;
// so moreProps and the variables below will receive some string just so the component will render
// without some accessor the properties will be undefined and it will throw an error
const moreProps = content.prop1.innerProp2.moreProps(); // moreProps = "some hard code string"
const differentProps = content.prop1.innerProp2.differentProps(); // differentProps = "some hard code string"
const anotherProp = content.prop1.innerProp2.anotherProp(); // anotherProp = "some hard code string"
const funcAtLevel2 = content.prop1.funcAtLevel2(); // funcAtLevel2 = "some hard code string"
const funcAtThisLevel = content.funcAtThisLevel(); // funcAtThisLevel = "some hard code string"
// other code...
return (
<div>
<span>{moreProps}</span>
<span>{differentProps}</span>
{/* more DOM structure here */}
</div>
);
}
Your code will work with this handler:
const handler = {
get() {
const f = () => 'value returned from function';
Object.setPrototypeOf(f, new Proxy({}, handler));
return f;
},
};
That is, if you define const content = new Proxy({}, handler);, then content.a.b.c.whatever() will return 'value returned from function'.
The key is returning a function that "extends" a Proxy using setPrototypeOf (I took the idea from invokable)

javascript print error when calling missing object property

I would like to write a proxy object to automatically print errors when calling some property in original object which is not found.
const proxyObjectFn = () => {
const _obj = Object.assign({}, originalObject);
const get = (key) => {
const value = _obj[key];
if (value === undefined) {
console.error(`${key} not found`);
}
return value;
};
return {
get,
};
};
const proxyObject = proxyObjectFn();
export default proxyObject;
// caller
proxyObject.get('someProperty')
This works, but is there any elegant way so that I can call through proxyObject.someProperty instead of proxyObject.get('someProperty')?
Update
Let me make it more specific. Actually I am writing a translation object.
Original object may be from json, like { "HELLO_KEY": "Hello World" }. I am to call like { label: _t.SOME_I18N_KEY } in UI display code, assuming _t is the proxy object above. I can print the warning to tell me there is missing translation.
You can use the Proxy object:
const handler = {
get: (obj, prop) => {
if(!obj.hasOwnProperty(prop)) console.error(`${prop} not found`);
return obj[prop];
}
};
const _t = new Proxy({ "HELLO_KEY": "Hello World" }, handler);
console.log(_t.HELLO_KEY);
console.log(_t.SOME_NONEXISTENT_KEY);

Unit test Vuex getters that depend on other getters

I've manage to test Vuex getters that are isolated from other code. I'm now facing some issues when a getter depends on other getters, see the following example:
getters.js
export const getters = {
getFoo(state) => prefix {
return `${prefix}: ${state.name}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
getters.spec.js
import { getters } = './getters';
const state = {
name: 'stackoverflow'
};
describe('getFoo', () => {
it('return name with prefix', () => {
expect(getters.getFoo(state)('name')).toBe('name: stackoverflow');
});
});
describe('getFancyNames', () => {
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, _getters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
]);
});
});
When the tested getter depends on other getter that has arguments this means that I've reference the original getter.getFoo on the mock, and this breaks the idea of mocking, since the tests start to have relation with each other. When the getters grow, and the dependency graph has several levels it makes the tests complex.
Maybe this is the way to go, just wanted to check that I'm not missing anything...
I agree with you that referencing the actual collaborator in your mock defeats the purpose of a mock. So instead I would simply directly return whatever you want your collaborator to return.
In your example, instead of doing something like this:
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
You would simply put in whatever getters.getFoo(state) would return:
const _getters = {
getFoo: 'foobar'
}
If you have a getter that takes an additional argument you would simply return a function that returns a constant:
const _getters = {
getFoo: x => 'foobar',
}
Since I'm using Jest there is an option in the jest mock function that let's specify the return value when called:
mockReturnValueOnce or mockReturnValue
More information can be found here: https://facebook.github.io/jest/docs/en/mock-functions.html#mock-return-values
Using the same code as in the question this could be solved like this:
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const getFoo = jest.fn()
getFoo.mockReturnValueOnce('foo: stackoverflow')
getFoo.mockReturnValueOnce('bar: stackoverflow')
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, { getFoo })).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
A cleaner way that I have found is to create your own mocked getters object. This only works if the getter uses the unaltered state like the question does.
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, mockedGetters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
Extra
In the case that you do need to call other getter functions just pass the mocked getters objects into another mocked getters object. It sounds worse than is actually is.
getters.py
export const getters = {
getBar(state) = { // new extra hard part!
return state.bar,
},
getFoo(state, getters) => prefix {
return `${prefix}: ${state.name} with some ${getters.getBar}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
const _mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
const mockedGetters = {
.._mockedGetters, // Use the mocked object!
getBar: getters.getBar(state, _mockedGetters), // We only overwrite what is needed
};
// continue down the line as needed!

Categories