DEV Community

Samaila Bala
Samaila Bala

Posted on

Testing Material UI form components

I used Material-UI for a side project and I ran into problems when trying to write a test for form components, this article illustrates the problem and how it was solved.

React Testing Library was used to write the tests which is a cool library for testing react components and also jest-dom which provides custom matchers that can be used to extend that of jest.

This is my App.js file

import React, { useState } from 'react';
import {
  FormControl,
  Input,
  InputAdornment,
  IconButton,
  InputLabel
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import Send from '@material-ui/icons/Send';

const useStyle = makeStyles((theme) => ({
  root: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
    width: 200
  }
}));
const App = () => {
  const classes = useStyle();
  const [input, setInput] = useState('');
  const [output, setOutput] = useState('');

  const handleChange = (event) => {
    setInput(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    setOutput(`Hello ${input}`);
  };

  return (
    <div data-testid="form">
      <h3>Material Form</h3>
      <FormControl className={classes.root}>
        <InputLabel htmlFor="adornment-send-title" data-testid="label">
          Enter Name
        </InputLabel>
        <Input
          id="adornment-send-title"
          type="text"
          value={input}
          onChange={handleChange}
          data-testid="nameInput"
          endAdornment={
            <InputAdornment position="end">
              <IconButton
                aria-label="submit answer"
                onClick={handleSubmit}
                data-testid="submit"
              >
                <Send className={classes.iconColor} />
              </IconButton>
            </InputAdornment>
          }
        />
      </FormControl>
      <p data-testid="output">{output}</p>
    </div>
  );
};

export default App;


Enter fullscreen mode Exit fullscreen mode

This is a screenshot of the App:

Screenshot

It renders a simple form component that has a text field and a button. When an input is entered and the button is clicked it displays a message.

This is the test for the App component:


import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import App from './App';

it('check if form displays', () => {
  const { getByTestId } = render(<App />);
  const form = getByTestId('form');
  const output = getByTestId('output');
  const label = getByTestId('label');
  const nameInput = getByTestId('nameInput');
  const submit = getByTestId('submit');

  expect(form).toBeInTheDocument();
  expect(output).toBeEmpty('');
  expect(label).toHaveTextContent('Enter Name');
  expect(nameInput).toHaveValue('');
  expect(submit).toBeInTheDocument();
});

it('should check if message is displayed when button is clicked', () => {
  const { getByTestId } = render(<App />);
  const output = getByTestId('output');
  const nameInput = getByTestId('nameInput');
  const submit = getByTestId('submit');

  expect(output).toBeEmpty('');
  expect(nameInput).toHaveValue('');

  fireEvent.change(nameInput, { target: { value: 'Sama' } });
  fireEvent.click(submit);
  expect(nameInput).toHaveValue('Sama');
  expect(output).not.toBeEmpty('');
});


Enter fullscreen mode Exit fullscreen mode

There are two tests in the file, the first checks if the page renders correctly and the second checks if the button performs the actions it is expected to perform.

Problem

When you run npm test you get the following errors:

Material form test error 1

Material form test error 2

From the test errors, we can clearly see that

 const nameInput = getByTestId('nameInput'); 
Enter fullscreen mode Exit fullscreen mode

returns undefined which is odd considering the component value is supposed to be an empty string

<Input
  required
  id="adornment-send-title"
  type="text"
  value={input}
  data-testid="nameInput"
  onChange={handleChange}
  endAdornment={
    <InputAdornment position="end">
      <IconButton
        type="submit"
        aria-label="submit answer"
        data-testid="submit"
      >
        <Send className={classes.iconColor} />
      </IconButton>
    </InputAdornment>
  }
/>
Enter fullscreen mode Exit fullscreen mode

Solution

So after being stuck for a while I decided to inspect the input element in the browser and realized that the Material UI TextField has a div wrapped around the input so I further explored by checking the TextField API documentation which can be accessed here. It confirmed my suspicion and further explained that to alter the props to the input element then inputProps has to be used so I moved the data-testid attribute to inputProps and it resulted in this:

<Input
  id="adornment-send-title"
  type="text"
  value={input}
  onChange={handleChange}
  inputProps={{
    'data-testid': 'nameInput'
  }}
  endAdornment={
    <InputAdornment position="end">
      <IconButton
        type="submit"
        aria-label="submit answer"
        data-testid="submit"
      >
        <Send className={classes.iconColor} />
      </IconButton>
    </InputAdornment>
  }
/>

Enter fullscreen mode Exit fullscreen mode

After that, I ran the tests again and viola the results of the tests were successful.

Material form test error 4

So, shameless plug this is the app created with Material UI, it is a lyric trivia app that tests knowledge of lyrics and this is the repo for it.

Top comments (6)

Collapse
 
kevinburton profile image
Kevin Burton

I would be interested in hearing your experience with submitting the form. I notice that you use 'fireEvent.click' which is the only way I could get it to work. I have seen others claim that fireEvent.submit works, but I have not had that experience. Thanks again for your post. It was most helpful.

Collapse
 
sama profile image
Samaila Bala

Hello Kevin, thanks for reading the article. I don't have any experience with using fireEvent.submit. However, I'll check it out to see how it works.

Collapse
 
goesbysteve profile image
Steve Jordan

@sama , have you found any way to have webpack remove the data-attribs from a production build or do you just leave them in?

Collapse
 
sama profile image
Samaila Bala

@goesbysteve I use CRA to bootstrap my React applications and l let it handle webpack issues so I have no idea if the data-attribs are removed from the production build. I'll check it out though. Thanks.

Collapse
 
projectkarol profile image
Karol Szczęsny

I found this solution :
npmjs.com/package/babel-plugin-jsx...

Collapse
 
kalaunlimited profile image
KaLaUnlimited

In my own example, a callback function, handeInput(), in my onChange prop of the Input component "is not recognized" and failing my tests. Any ideas on how to fix this?