【问题标题】:Why can't React test read the 'checked' property为什么 React 测试无法读取 \'checked\' 属性
【发布时间】:2023-01-20 00:05:28
【问题描述】:

我正在尝试测试在用户输入查询、选择复选框选项并按下搜索按钮后,React 组件是否从外部 API 呈现食谱。我期待呈现一个带有文本“Recipe”的按钮。当我运行测试时,返回以下错误:

无法读取未定义的属性(读取“已检查”)。

let str = "";                                                                                                                
      29 |     for (let i = 0; i < boxes.length; i++) {                                                                                     
    > 30 |       if (boxes[i].checked) {
         |                    ^
      31 |         str = str + boxes[i].value;
      32 |       }
      33 |     }

测试卡在 SearchPage 组件(如下提供)中的“复选框”功能中:

import './SearchPage.css';
import {useState, useRef} from 'react';
import axios from 'axios';
import RecipeImgs from '../RecipeImgs/RecipeImgs.js';
import {Link} from 'react-router-dom';

function SearchPage() {

  const [query, setQuery] = useState([]);
  const [hits, setHits] = useState([]);
  const [check, setCheck] = useState([]);
  const scrollResults = useRef(null);
  
  // error comes from the conditional statement below
  let checkbox = () => {
    let boxes = document.forms[0];
    let str = "";
    for (let i = 0; i < boxes.length; i++) {
      if (boxes[i].checked) {
        str = str + boxes[i].value;
      }
    }
    setCheck(str);
  };

  const recipes = async () => {
    const url = `http://localhost:8000/food?q=${query}${check}`
    try {
      const request = await axios.request(url);
      if (request.data.hits === 0) {
        alert ('No results! Please try again.');
      }
      setHits(request.data.hits);
    }
    catch (e) {
      alert ('Oh no! Only 10 searches per minute please. Try again in 60 seconds.');
      console.log(e);
    }
  };

  //when form is submitted
  const onSubmit = (e) => {
    e.preventDefault();
    recipes();
    //scrolls down to results
    setTimeout(function() {window.scrollTo({
      top: scrollResults.current.offsetTop,
      behavior: 'smooth'
    })}, 500);
  }

  return (
    <div className='search-container'>
      <div className='nav-header'>
        <Link to = "/foodquest/"><div className="nav-logo"><img src={tomato} alt="tomato" /></div></Link>
        <nav className="nav">
          <ul>
            <li className="nav-home"><Link to="/foodquest/"><img src={home} alt="home icon" />Home</Link></li>
            <li><Link to="/foodquest/search"><img src={bowl} alt="food icon" />Search</Link></li>
            <li><Link to="/foodquest/random"><img src={random} alt="dice icon" />Random</Link></li>
            <li><Link to="/foodquest/about"><img src={info} alt="info icon" />About</Link></li>
          </ul>
        </nav>
      </div>
      <div className='search-input-container'>
        <h1>Recipe Search</h1>
        <form className='search-form' onSubmit={onSubmit}>
          <input className='search-field' type='text' placeholder='Enter ingredients' 
          value={query} onChange={(e)=> setQuery(e.target.value)}/>
          <div className='checkbox-container'>
            <div className='checkboxes'>
              <label className='check-option'><input type="checkbox" value="&health=dairy-free" onChange={checkbox}/>dairy-free</label>
              <label className='check-option'><input type="checkbox" value="&health=gluten-free" onChange={checkbox}/>gluten-free</label>
              <label className='check-option'><input type="checkbox" value="&health=peanut-free" onChange={checkbox}/>peanut-free</label>
              <label className='check-option'><input type="checkbox" value="&health=tree-nut-free" onChange={checkbox}/>tree-nut-free</label>
              <label className='check-option'><input type="checkbox" value="&health=wheat-free" onChange={checkbox}/>wheat-free</label>
              <label className='check-option'><input type="checkbox" value="&health=soy-free" onChange={checkbox}/>soy-free</label>
            </div>
            <div className='checkboxes'>
            <label className='check-option'><input type="checkbox" value="&health=fish-free" onChange={checkbox}/>fish-free</label>
              <label className='check-option'><input type="checkbox" value="&health=shellfish-free" onChange={checkbox}/>shellfish-free</label>
              <label className='check-option'><input type="checkbox" value="&health=egg-free" onChange={checkbox}/>egg-free</label>
              <label className='check-option'><input type="checkbox" value="&health=vegan" onChange={checkbox}/>vegan</label>
              <label className='check-option'><input type="checkbox" value="&health=vegetarian" onChange={checkbox}/>vegetarian</label>
              <label className='check-option'><input type="checkbox" value="&health=paleo" onChange={checkbox}/>paleo</label>
            </div>
          </div>
          <button type='submit' ref={scrollResults}>Get Recipe</button>
        </form>
      </div>
      <div className='search-result-container'>
        {hits.map((index) => {
          return <RecipeImgs props={index} key={index.recipe.uri} />
        })}
      </div>
    </div>
  );
}

export default SearchPage;

搜索页面.test.js:

import SearchPage from "../SearchPage";
import {render, screen, waitFor} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {BrowserRouter as Router} from 'react-router-dom';

test("render recipes from API response on SearchPage component", async () => {
  const user = userEvent.setup()
  render(
    <Router>
      <SearchPage />
    </Router>
  ) 
  // target input, checkbox, and search button
  const query = screen.getByPlaceholderText('Enter ingredients') ;
  const check = screen.getByText('dairy-free');
  const button = screen.getByRole('button');
  // simulate typing 'apple' into input and clicking the 'dairy-free' checkbox
  await user.type(query, 'apple');
  await user.click(check);

  await user.click(button); 

  await waitFor(() =>  
    expect(screen.getAllByText('Recipe')[0]).toBeInTheDocument()
  )   
});

如果我删除选中复选框的代码“userEvent.click(check)”,它就会通过。该组件手动工作,但我无法让测试工作。在此先感谢您的帮助。

【问题讨论】:

  • boxes 不是指输入元素的集合,而是指表单本身。此外,与您的问题无关,但 userEvent 返回一个承诺,因此您应该等待它。
  • @Terry:复选框功能手动工作,因为所需的状态变量已在点击时正确更新。什么变化可以让该功能手动工作而不妨碍测试?另外,感谢您对 userEvent 的更正。

标签: reactjs react-testing-library


【解决方案1】:

似乎 document.forms[0] 包含了除复选框之外的元素,这些元素已经放弃了测试。我为复选框分配了一个“.check-input”类,并使用 querySelectorAll() 将它们作为目标,它起作用了。

    let checkbox = () => {
    let boxes = document.querySelectorAll('.check-input');
    let str = "";
    for (let box of boxes) {
      if (box.checked) {
        str += box.value;
      }
    }
    setCheck(str);
  };

【讨论】:

    猜你喜欢
    • 2021-06-09
    • 2019-08-09
    • 2021-05-19
    • 2020-10-04
    • 2017-12-30
    • 2014-10-31
    • 2021-01-13
    • 1970-01-01
    • 2017-06-27
    相关资源
    最近更新 更多