Covering Anonymous Function Arguments in Unit Tests - javascript

I have multiple situations in code where we pass a function as an argument when creating an object (like so in onPress):
<Touchable
onPress={() => Linking.openURL(formatUrl(url))}
noContainer={true}>
{children}
</Touchable>
When it comes down to it, this is a snippet in a larger component getting rendered. As it's not actually a method of the component, I'm not especially interested in testing it here - it will get tested as part of another component if needed.
However, the code coverage report comes back with an indication that the function is uncovered.
Is there a way to satisfy this coverage - either by testing this function, or ignoring all functions passed in such a way (anonymously, as arguments)?

Assuming you are using Jest, you can set your Jest mock to call the argument that it receives, so:
const componentProps = {
onPress: jest.fn(func => func());
}
So the mocked onPress function, in this case, will receive your anonymous function and call it. Coverage will show that the anonymous function was called.
Hope it helps :)

First, I'd like to advise against exempting lines from coverage, especially if you're being paid to write this code and unit test it.
One thing I've found that works is to write a void function that calls your function with your desired arguments.
if you have:
onClick={ () => function(argument) }
and Jest isn't covering it, try:
const functionWithArgs = () => function(argument)
onClick={functionWithArgs}
I use this a lot for modals:
const [isModalOpen, setModalOpen] = React.useState(false)
const toggleModal = () => setModalOpen(!isModalOpen)
onClick={toggleModal}
But the same principle can be applied to a much more complex function. And it works!

Related

React Testing Library: OnClick isn't working in a child component

I have a test like this:
it('Side effect should trigger', async ()=>{
await act(async () => {
let container = render(<Grandparent/>);
const btn = container.findByTestId('childBtn');
fireEvent.click(btn)
});
const changedMsg = await container.findByText('Changed');
expect(changedMsg).toBeInTheDocument();
})
My components look like this:
Grandparent:
const Grandparent = ()=>{
const [labelText, setLabelText] = useState('Unchanged');
return (<Parent labelText={labelText} setLabelText={setLabelText}/>);
}
Parent:
const Parent = ({labelText, setLabelText})=>{
return (<div>
<label>{labelText}</label>
<Child setLabelText={setLabelText} />
</div>)
}
Child:
const Child = ({setLabelText}) =>{
return(<div>
<Button data-testid='childBtn' onClick={()=>setLabelText('Changed')}/>
</div>)
}
My issue is that the assertion in my unit test fails. I've logged the return value from findByTextId('childBtn') and I am indeed getting a button (something like <button class='MuiButtonBase-root' data-testid='childBtn'></button>), but the side effect from clicking it is not going through. I added some logging in the onClick function and it never showed up when I re-ran the test, so the issue is that the onClick handler isn't firing at all.
How do I fix this? Is trying to test something that involves side effects from a bunch of nested components like this not feasible in the first place?
EDIT: I think you have some code mistakes in your unit test since you are mixing react-testing-library API and testing-library, try this:
it("Side effect should trigger", async () => {
const {findByTestId, findByText} = render(<Grandparent />);
fireEvent.click(await findByTestId("childBtn"));
const changedMsg = await findByText("Changed");
expect(changedMsg).toBeInTheDocument();
});
});
I did not test it, but it should work.
act() wrapper is not needed in react-testing-library since the render method is using it internally.
With react-testing-library you dont' have to care about implementation details of your components, nor you care to test internal state, etc... You just test "What the end user sees on screen in response to certain events". If you need to test functions, like fetch, interactions with global object, etc... you need to mock all those methods.
The philosophy of this approach is to make your unit tests ( which are never funny to write ) so that they won't have to change too often even if you are going to refactor your React Components. You just care about what it' s being rendered as DOM nodes and that's what you have to test.
NOTE: In the code you pasted there are typos btw, props have to be destructured in your code if you want to use them directly without doing props.prop, like : const Child = ({setLabelText}) =>{ .

How to call React component function from Electron main.js using contextBridge

As the title states, I would like to call a function defined in one of my React components from the Electron main.js file. I know how to do it the other way around, where for example the user clicks a button and it calls a function in main.js and then returns to the React component. I need it to be the opposite, where main.js calls the React function and then returns the result to main.
I cannot simply have all the functionality in main.js since I cannot pass DOM objects to main from React (it has resulted in the error "object cannot be cloned"). So I figure it shouldn't be too hard to just send the request to React, have the React side of my app do the stuff, and then return the resulting string back to main.
I have found a number of other similar posts here where various solutions are offered, but none specifically for this exact problem. #1 exposes ipcRenderer which is a bad security practice I can't afford. #2 and #3 only explain it for vanilla Javascript, not React components. And while #4 does deal with React, it does not deal with Electron, and it is designed for React classes (I'm using functional components).
My resulting attempt looks like this mess:
const [mounted, setMounted] = useState(false)
if(!mounted){
window.reactFunction = myFunction;
}
useEffect(() =>{
setMounted(true)
},[])
window.electron.receive("fromMain", async () => {
return myFunction();
});
async function myFunction()
{
return "result";
}
However, the function instead gets called during rendering and is not called by the contextBridge. Here is my preload.js:
contextBridge.exposeInMainWorld('electron', {
receive: async (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));
}
}
});
For some reason the preload.js script is not executed. And here is main.js:
const result = window.webContents.send('fromMain');
I have also found this discussion where it is made clear the security issues associated with calling ipcRenderer directly and so I would like to use contextBridge (which I'm already using for calling main functions from React anyway). It's also where my preload.js code comes from. But that example doesn't use React either.
Is this even possible? Or am I supposed to go about this problem a different way?
Looks like I made a typing mistake on this line:
return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));
In this line I'm returning the function, so the function itself is not getting called. Remove the "return" and it will work. "Await" doesn't seem to make a difference.
However, once you do call the function, it can't actually return anything. So you need to make another call from renderer back to main, like this:
window.electron.receive("fromMain", async () => {
const returnValue = await myFunction();
window.electron.sendReturnValue(returnValue);
});
async function myFunction()
{
return "result";
}
Unless there is a still better way of doing it, but this solves my issue for now, and hopefully helps others.

Testing an isolated function of a component in React Jest

I am fairly new to Jest and Enzyme and I stumbled a cross a problem:
I have a Component which renders Children and also calls a method of those children. I achieve that by using refs. I call these functions something like:
somefunction = () => {
this.myReference.current.childFunction();
this.doSomethingOther();
}
I now want to test the function somefunction. I want to check if the doSomethingOther function was called. Using a shallow render I cannot achieve that. The test would succeed if this.myReference.current.childFunction(); wasn't called. Jest cannot know it because it only renders shallow and therefore throws an error.
I may be missing the full understanding. I wonder if someone has a Idea to test this function without using mount.
Take a look at the below code where I shallow render a component and then get the class instance and mock the required functions. Now when we call somefunction, we check if doSomethingOther has been called. (Assumption you are using jest+enzyme)
const wrapper = shallow(<Comp />);
const inst = wrapper.instance();
inst.myReference = {
current: {
childFunction: jest.fn()
}
}
inst.doSomethingOther = jest.fn();
inst.somefunction();
expect(inst.doSomethingOther).toHaveBeenCalled();

Calling a method vs using a function to call a method

Suppose we have a method inside a class like this
class Blog extends Component {
postClicked = (id) => {
this.setState({selectedPostId: id})
}
render () {
const newPosts = this.state.posts.map(el => {
return <Post key={el.id}
title={el.title}
author={el.author}
onClick={this.postClicked(el.id)}/>
})
return
//something
{post}
}
}
}
Now, What is the difference between calling the handler like this
onClick={this.postClicked(el.id)} and onClick={() => this.postClicked(el.id)}
Would appreciate if someone can tell me the difference in general
after Ecmascript 6 javascript was introduced with is arrow function link
here ()==>{//code} is a similar as a function() or anonymous function
tell me if you find out what you want
The first option, "this.postClicked(el.id)", will actually call the method, "this.postClicked", with the "el.id" argument, each time the component renders (probably not what's intended).
The second option, "() => this.postClicked(el.id)", will only call the method, "this.postClicked", with the "el.id" argument, when "Post" is clicked.
Overall, if you can find a way to put the "el.id" argument into an "id" or "name" prop on the component
<Post id={el.id} />
then you can do:
<Post
id={el.id}
onClick={this.postClicked}
/>
this.postClicked = (event) => {
const { id } = event.target;
...
}
This last option avoids the use of an unnamed function. If you use an unnamed function, it will cause unnecessary re-renders. React cannot tell that an unnamed function is the same when it's checking whether or not it should re-render, by considering if the props of a component have changed. It considers the unnamed functions to be a new prop each time it checks, causing an unnecessary re-render each time.
Overall, it won't break your app, but it slows down performance slightly if you do it enough. It comes up especially if you start using React Motion (you'll really notice a difference there). It's best to avoid unnamed functions if possible.
you can read this blog it wil clear the things https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Differences are,
First method is a wrong implementation and it wont give the intended result, where as second one will work.
In the first method you are making a function call, in second one you are assigning a function's signature to onClick.
It is like the combination of below two statements.
var variableName = function(){//some content};
onClick={variableName}
It looks like you question has already been answered. Just a side note though: remember that when assigning your method with an arrow function
onClick={ () => this.method() }
a new anonymous function is created on every re-render. So if the method doesn't need any arguments, it's better to reference the method directly (without parentheses so it's not invoked).
onClick={ this.method }
The first will call the function every time render is done.
The second will do what you want - call it onClick.

Check if functions have been called in unit test

Hi I'm trying to write some unit tests in Jest for a module I write, but kind of stuck currently and need some advice how to continue.
export const submitOrder = async (body, key) => {
const clientRepo = new ClientRepository(db)
const companyRepo = new CompanyRepository(db)
const company = await getCompanyByKey(
companyRepo,
key
);
const client = await createClient(
clientRepo,
body
);
await addClientToCompany(
companyRepo,
client.id,
company.id
);
.. More things
}
I can easily test each function(getCompanyByKey, createClient & addClientToCompany) by passing down a mocked repository.
But I would also like to test my "flow" of the submitOrder function, by checking if my repository functions have been called. But I would then need the instance of each repository, which I don't instantiate until my submitOrder function.
Something like this, which is similar how I unit test my functions.
jest.mock('../repositories/ClientRepository');
jest.mock('../repositories/CompanyRepository');
test('should be able to submit an order', async () => {
const apiKey = 'mocked-super-key';
const body = getMockData();
const result = await submitOrder(body, apiKey);
expect(result).toMatchSnapshot();
expect(CompanyRepository.findByKey).toHaveBeenCalled();
expect(ClientRepository.create).toHaveBeenCalled();
expect(CompanyRepository.addClient).toHaveBeenCalled();
});
Do you have any tips of how I can test if my repositories have been called?
The problem you describe is one of the motivating factors behind dependency injection.
As a single example: your submitOrder() code uses new to directly instantiate a client repository of the specific implementation ClientRepository. Instead, it could declare that it has a dependency - it needs an object that implements the interface of a client repository. It could then allow for such an object to be supplied by the surrounding environment (a "dependency injection container" in buzzword-ese). Then during testing you would create and provide ("inject") a mock implementation instead of the real implementation.
This has the added benefit that if you ever have to be able to select between multiple "real" implementations, you're already set up to do that too.
There are many ways to achieve this. It can be as simple as a design pattern, or for a more complete solution you could use a dependency injection framework.
If you absolutely cannot refactor your code for this practice, then JavaScript is dynamic enough that you can probably cobble together a way to intercept the invocation of new and thereby simulate dependency injection.
You can pass a mock implementation factory as a second parameter to jest.mock, as described in the docs.
You can use this to mock out the methods that you want to check to have been called.
Try this:
jest.mock('../repositories/CompanyRepository', () => {
findByKey: jest.fn(),
addClient: jest.jn()
});
const mockCreate = jest.fn();
jest.mock('../repositories/CompanyRepository', () => class {
create(...args) {
mockCreate(...args);
}
});
test('should be able to submit an order', async () => {
const apiKey = 'mocked-super-key';
const body = getMockData();
const result = await submitOrder(body, apiKey);
expect(result).toMatchSnapshot();
expect(CompanyRepository.findByKey).toHaveBeenCalled();
expect(ClientRepository.create).toHaveBeenCalled();
expect(CompanyRepository.addClient).toHaveBeenCalled();
});
Since CompanyRepository is created with “new”, we use a class definition in this case and pass in a mock function that is called when the “create” method is invoked.

Categories

Resources