I asked Chat GPT to Create ReactJS Testing Component, how accurate is it?
This story continues my previous article about ReactJS Component Testing which you can see here.
ChatGPT seems to be very much talked about in recent times, appearing like the latest technology that can do anything. and of course it’s a bit scary too if you see its ability.
I got this idea after having discussions with my team at work, regarding using ChatGPT as a tool to help us during the development, in my heart I thought “Wow, how smart is ChatGPT?!”
Starting from helping refactoring code, to answering some logic questions in programming languages. And of course, one thing it can do is generate code for automation testing, Wow!
From that discussion is, my reason for creating a following article for ReactJS Component Testing. This time the main topic is “How accurate” is ChatGPT in generating the automation test code for the Component Login that we made yesterday.
How To Use
First of all, we can visit the ChatGPT site in here, you will be see the main page like this:
Remember, we have Login Component as shown as code below :
//in Login.tsx
import React, { useState } from "react";
import "../styles/styles.css";
interface LoginProps {
username?: string | undefined;
password?: string | undefined;
onSubmit? : () => void;
}
const Login = (props : any) => {
const [disabled, setDisabled] = useState(true);
const [user, setUser] = useState<LoginProps>();
const handleChange = (e: any) => {
const name = e.target.name;
const value = e.target.value;
setUser((values) => ({ ...values, [name]: value }));
if (user) {
setDisabled(false);
} else {
setDisabled(true);
}
};
const handleSubmit = () => {
alert(user);
};
return (
<div className="login-container" role="login-component">
<form onSubmit={props.onSubmit ? props.onSubmit : handleSubmit}>
<label>Username</label>
<input name="username" value={props.username} onChange={handleChange} role="uname" />
<label>Password</label>
<input
name="password"
type="password"
onChange={handleChange}
value={props.password}
role="password"
/>
<button
role="btn"
className={`${disabled ? "disabled" : ""}`}
disabled={disabled}
type="submit"
>
Submit
</button>
</form>
</div>
);
};
export default Login;
We can use that component as target for component testing in this session. Now, back to Chat GPT site, we can use the following command to generate component testing for our components.
create test case for this code :
{ your_code_will_be_here }
So, the complete command will be like this :
create test case for this code :
import React, { useState } from "react";
import "../styles/styles.css";
interface LoginProps {
username?: string | undefined;
password?: string | undefined;
onSubmit? : () => void;
}
const Login = (props : any) => {
const [disabled, setDisabled] = useState(true);
const [user, setUser] = useState<LoginProps>();
const handleChange = (e: any) => {
const name = e.target.name;
const value = e.target.value;
setUser((values) => ({ ...values, [name]: value }));
if (user) {
setDisabled(false);
} else {
setDisabled(true);
}
};
const handleSubmit = () => {
alert(user);
};
return (
<div className="login-container" role="login-component">
<form onSubmit={props.onSubmit ? props.onSubmit : handleSubmit}>
<label>Username</label>
<input name="username" value={props.username} onChange={handleChange} role="uname" />
<label>Password</label>
<input
name="password"
type="password"
onChange={handleChange}
value={props.password}
role="password"
/>
<button
role="btn"
className={`${disabled ? "disabled" : ""}`}
disabled={disabled}
type="submit"
>
Submit
</button>
</form>
</div>
);
};
export default Login;
And yes, here the feedback from Chat GPT:
Here’s the result of the generated code:
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Login from './Login';
describe('Login component', () => {
it('should render the component', () => {
const { getByLabelText, getByRole } = render(<Login />);
const usernameInput = getByLabelText('Username');
const passwordInput = getByLabelText('Password');
const submitButton = getByRole('btn');
expect(usernameInput).toBeInTheDocument();
expect(passwordInput).toBeInTheDocument();
expect(submitButton).toBeInTheDocument();
});
it('should call handleChange when input values change', () => {
const { getByLabelText } = render(<Login />);
const usernameInput = getByLabelText('Username');
const passwordInput = getByLabelText('Password');
fireEvent.change(usernameInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'testpassword' } });
expect(usernameInput.value).toBe('testuser');
expect(passwordInput.value).toBe('testpassword');
});
it('should call handleSubmit when form is submitted', () => {
const { getByRole } = render(<Login />);
const submitButton = getByRole('btn');
fireEvent.click(submitButton);
expect(submitButton).toBeDisabled();
});
it('should call onSubmit if passed in props', () => {
const onSubmit = jest.fn();
const { getByRole } = render(<Login onSubmit={onSubmit} />);
const submitButton = getByRole('btn');
fireEvent.click(submitButton);
expect(onSubmit).toHaveBeenCalled();
});
});
From the result, i copied all of the code and i Create new file for Testing Login Page Component with name LoginGenerated.test.tsx. Here’s the comparison between our test cases and Generated Chat GPT test cases :
Surprisingly, the cases between our test cases and generated test cases is quite similar, wow!. Next, let’s see about the result when we running the test file. You can execute this line to running only generated test.
npm run test -- LoginGenerated.test.tsx
Maybe from here, we have a little disappointed because the test results turned out to be a failure. However, if we look at the method used in the test one more time, you will see a slight difference. And we will discuss now.
getByLabelText vs getByRole
From the test case file, there is a slight difference in identifying the component. As we usually use getByRole with add the “role” attribute at the input field, this generated test case uses getByLabelText as a way to identify the field component based on label, not role. And, based on the message we received, that label in our component does not refer to any component field.
// the message
TestingLibraryElementError: Found a label with the text of: Username,
however no form control was found associated to that label.
Make sure you're using the "for" attribute or "aria-labelledby"
attribute correctly.
Solution
we can slightly refactor our component with adding htmlFor at the label, and id at the field component, so the result will be like below:
// adding htmlFor at label, and id at the input field
<label htmlFor="username">Username</label>
<input
id="username"
name="username"
value={props.username}
onChange={handleChange}
role="uname"
/>
<label htmlFor="password">Password</label>
<input
id="password"
name="password"
type="password"
onChange={handleChange}
value={props.password}
role="password"
/>
and let’s run the test again:
From the results of our refactor, at least test cases 1 and 2 have been successfully executed, then we will discuss the fourth test case.
Why onSubmit case Failed ?
Actually the logic testing is almost very similar to the results we made before. Both of them use jest.fn( ) for their mock function. Then what made it fail?
As in the component we created, the submit button will be disabled when the user has not entered a username and password, or the field username and password is still empty. So with the condition that the button is still disabled, of course it’s not possible to trigger handleSubmit or onSubmit.
Solution
We will refactor the logic test, this time by adding a mock action so that the user types in the username and password fields, so that it will trigger the button so that it is not disabled.
it('should call onSubmit if passed in props', () => {
const onSubmit = jest.fn();
// adding getByLabelText to identify the username adn password input
const { getByRole, getByLabelText } = render(<Login onSubmit={onSubmit} />);
const usernameInput = getByLabelText('Username');
const passwordInput = getByLabelText('Password');
// mock user action to typing in the field, so the field will be not empty
fireEvent.change(usernameInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'testpassword' } });
const submitButton = getByRole('btn');
fireEvent.click(submitButton);
expect(onSubmit).toHaveBeenCalled();
});
And here’s the result :
Conclusion
From the results of our discussion above, we can conclude that we can use ChatGPT as a tool that helps us in the development process so that it runs faster and more effectively. But still, we need to do a little refactoring on the results of this ChatGPT, considering this is still being an AI under development, one would definitely find some imperfections in the output.
Apart from that, there are also a number of points that we can take from the result of this ChatGPT:
1. For the initialization of test cases, of course this is very useful in speeding up the process of making automation tests.
2. In terms of case development, the results of ChatGPT can still be said to be fairly basic results, so we still need to develop our test cases in more advanced cases.
3. We can also make the results of ChatGPT to doing semi-TDD, when running the generated test case from ChatGPT and the result is still fail, maybe there is something we need to fix in our code or component.
Maybe this is enough for our interesting discussion about ChatGPT, an AI tool that quite helpful for developers. Hopefully this information can be useful for all readers, thank you.
If you like my article, maybe you can visit my other story.