由于保留所有dom测试,因此React单元测试失败

时间:2020-07-19 19:36:53

标签: reactjs unit-testing react-redux react-testing-library

我有一个笔记列表组件,该组件连接到redux并呈现项目列表。我为组件创建2个单元测试,以测试组件何时具有列表以及何时为空。第一次单元测试列表上没有注释是成功的,但是当列表上有多个注释时,它会保留先前的单元测试dom,并且测试失败。

enter image description here

enter image description here

import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import configureStore from 'redux-mock-store';
import NoteList from '../../../../../src/components/note/note-list-children/NoteList';

const tasks = [
  {
    title: 'e2e',
    content: 'Learning E2E',
    date: '2020/03/05',
    id: 'evRFE5i5',
    done: false,
  },
  {
    title: 'unit',
    content: 'Learning Unit Test',
    date: '2020/03/04',
    id: 'FTSTSrn7',
    done: false,
  },
];

describe('No notes on list.', () => {
  const mockStore = configureStore();
  const initialState = {
    tasksReducer: [],
    mainReducer: {},
  };
  const store = mockStore(initialState);

  beforeEach(() => {
    render(<NoteList store={store} tasks={[]} />);
  });

  it('Should having no notes on the list (showing a proper message).', () => {
    expect(screen.getByText(/No notes yet./i)).toBeTruthy();
  });
});

describe('Having multiple notes on the list.', () => {
  beforeEach(() => {
    const mockStore = configureStore();
    const initialState = {
      tasksReducer: [],
      mainReducer: {},
    };
    const store = mockStore(initialState);
    render(<NoteList store={store} tasks={tasks} />);
  });

  it('Should not showing empty massege for note lists', () => {
    screen.debug();
    expect(screen.queryByText(/No notes yet./i)).not.toBeInTheDocument();
  });
});

注释列表组件

import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link';

// Redux
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { deleteTask, doneTask } from '../../../redux/actions/actionTasks';
import { activeMain } from '../../../redux/actions/actionMain';

// Materail UI
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import Checkbox from '@material-ui/core/Checkbox';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';

// Component
import DeleteNotes from './DeleteNotes';

const NoteList = props => {
  const { tasks, active } = props;

  const handleActiveMain = item => {
    props.activeMain('singleNote', item.title, item.content, item.date, item.id, item.done);
  };

  const handleToggle = item => {
    if (item.done === false) {
      props.doneTask(true, item.id);
    } else {
      props.doneTask(false, item.id);
    }
  };

  const handleDeleteNote = id => {
    props.deleteTask(id);

    if (active.id == id || tasks.length === 1) {
      props.activeMain('create');
    }
  };

  const handleEditNote = item => {
    props.activeMain('edit', item.title, item.content, item.date, item.id, item.done);
  };

  return (
    <div className="list-tasks" data-test="note-list">
      <List className="note-list">
        {tasks.length ? (
          tasks.map(item => (
            <ListItem ContainerProps={{ 'data-test': 'note-item' }} key={item.id} dense button>
              <ListItemIcon>
                <Checkbox
                  edge="start"
                  onClick={() => handleToggle(item)}
                  checked={item.done === true}
                  tabIndex={-1}
                  disableRipple
                  inputProps={{ 'aria-labelledby': item.id, 'data-test': 'note-check' }}
                />
              </ListItemIcon>
              <Link href="/" as={process.env.BACKEND_URL + '/'}>
                <ListItemText
                  onClick={() => handleActiveMain(item)}
                  className={item.done === true ? 'done' : ''}
                  id={item.id}
                  primary={<span data-test="note-title">{item.title}</span>}
                  secondary={<span data-test="note-date">{item.date}</span>}
                />
              </Link>
              <ListItemSecondaryAction>
                <IconButton
                  onClick={() => handleDeleteNote(item.id)}
                  edge="end"
                  aria-label="delete"
                >
                  <DeleteIcon data-test="note-delete" />
                </IconButton>
                <Link
                  href={`/edit?id=${item.id}`}
                  as={`${process.env.BACKEND_URL}/edit?id=${item.id}`}
                >
                  <IconButton onClick={() => handleEditNote(item)} edge="end" aria-label="edit">
                    <EditIcon data-test="note-edit" />
                  </IconButton>
                </Link>
              </ListItemSecondaryAction>
            </ListItem>
          ))
        ) : (
          <p data-test="no-note-yet">No notes yet.</p>
        )}
      </List>
      {tasks.some(item => item.done === true) === true && <DeleteNotes selectedNotes={tasks} />}
    </div>
  );
};

NoteList.propTypes = {
  active: PropTypes.object.isRequired,
  tasks: PropTypes.array.isRequired,
  deleteTask: PropTypes.func.isRequired,
  activeMain: PropTypes.func.isRequired,
  doneTask: PropTypes.func.isRequired,
};

const mapDispatchToProps = dispatch => {
  return {
    deleteTask: bindActionCreators(deleteTask, dispatch),
    activeMain: bindActionCreators(activeMain, dispatch),
    doneTask: bindActionCreators(doneTask, dispatch),
  };
};

const mapStateToProps = state => {
  return {
    active: state.mainReducer,
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(NoteList);

0 个答案:

没有答案