簡體   English   中英

如何為 React useEffect 中的代碼編寫 Jest-enzyme 測試用例,其依賴項在其子組件中更新

[英]How to write a Jest-enzyme test case for code in React useEffect whose dependencies are updated in its child component

我有一個用於過濾功能的父組件

export const FilterComponent = ({data}) {
  const [priceRange, setPriceRange] = React.useState({});
  const [amenities, setAmenities] = React.useState([]);

  React.useEffect(() => {
    if (priceRange.min) {
      // code to filter based on priceRange
    }

    if(amenities.length) {
      // code to filter based on amenities
    },[priceRange, amenities])

  return (
          <select id="price" value={priceRange} onChange={(event) => setPriceRange(JSON.parse(event.target.value))}>
            <option value="">Price Per Night</option>
            <option value='{"min":0,"max":99}'>Less than $99</option>
            <option value='{"min":100,"max":299}'>$100 to $299</option>
            <option value='{"min":300,"max":499}'>$300 to $499</option>
            <option value='{"min":500}'>More than $500</option>
          </select>

    <MoreFiltersComponent setAmenities={setAmenities} amenities={amenities} />}
  )
}

我使用這個父組件來顯示過濾器,比如價格范圍和用戶可以在屏幕上查看的其他過濾器,並為它們添加了測試用例。 我為此創建了一個子組件,MoreFiltersComponent,以顯示許多其他過濾器,用戶可以在單擊“更多過濾器”按鈕后查看這些過濾器。 便利設施是 MoreFilters 的一部分,但我在父組件中為它創建了狀態並將其作為道具傳遞,以便我可以在有任何更新時運行父組件的 useEffect 來檢查所有過濾器。

export const MoreFiltersComponent = ({setAmenities, amenities}) => {

  const updateValue = (event, state, updateState) => {
   // code to update the state
  }
 
 return (
   <input type="checkbox" name="ac" id="ac" value="ac" onChange={(event) => updateValue(event, amenities, setAmenities)} />
 )
}

這是我創建的測試用例,用於測試運行良好的價格范圍過濾器

const setPriceRangeSpy = jest.fn();
const mockUseEffect = () => {
    useEffect.mockImplementationOnce((f) => f());
  };

it('should render the correct results for selected price range', () => {
    const event = {
      target: { value: '{"min": 0, "max": 99}' },
    };

    useStateSpy.mockImplementationOnce(() => [{}, setPriceRangeSpy]);
    renderedModule = shallow(<FilterComponent {...props} />);
    mockUseEffect();

    renderedModule.find('#price').simulate('change', event);
    
    expect(setPriceRangeSpy).toHaveBeenCalledWith({ min: 0, max: 99 });
    expect(renderedModule).toMatchSnapshot();
 }

我嘗試了一個類似的便利設施測試用例,但它不起作用。 我無法模擬 onChange 事件,因為該元素不在此組件中,因此 useEffect 中的代碼行在我根據便利條件進行過濾的地方沒有被發現。 有沒有辦法我也可以覆蓋這些代碼行?

 const setAmenitiesSpy = jest.fn();
 const mockUseEffect = () => {
    useEffect.mockImplementationOnce((f) => f());
  };

 it('should render the correct results when there is no max price', () => {
   useStateSpy.mockImplementationOnce(() => [["ac", "tv", "wifi"], setAmenitiesSpy]);
   renderedModule = shallow(<FilterComponent {...props} />);
   mockUseEffect();

   expect(setAmenitiesSpy).toHaveBeenCalledWith(["ac", "tv", "wifi"]);

 }

對於設施測試用例,我收到此錯誤:

預期:[“交流”,“電視”,“無線網絡”]

通話次數:0

我無法模擬 onChange 事件,因為該元素不在此組件中,因此 useEffect 中的代碼行在我根據便利條件進行過濾的地方沒有被發現。 有沒有辦法我也可以覆蓋這些代碼行?

謝謝!

首先,我認為你最好不要嘲笑useStateuseEffect 這絕對是實現細節,雖然您在技術上可以做到這一點 - 您可能已經達到了這一點 - 使任何更改越來越難以包含在測試中。 不告訴你,如果你出於任何原因決定重塑你的狀態(比如,讓它的一部分來自 props 或上下文),你將需要完全重寫你的測試。 這不好。

其次,因為shallow()仍然不能很好地useEffect一起useEffect 所以只需使用mount() 是的,這意味着您將深入渲染所有嵌套組件……直到您明確模擬它們! 看:

import { MoreFiltersComponent } from '../path/to/MoreFiltersComponent.jsx';

jest.mock('../path/to/MoreFiltersComponent.jsx', () => ({
 MoreFiltersComponent: function MoreFiltersComponent() {
  // so this is mocked React component that always returns null
   return null; 
 } 
}));

....
 const wrapper = mount(.....);
 // you still can search for this component, even if it's mocked and always returns null in render
 wrapper.find(MoreFiltersComponent) 

此外,您很可能需要react/test-utils act() 一些酶的助手(如simulate()已經調用了它,而其他酶可能沒有調用。 此方法確保所有相關的useEffect / useLayoutEffect將以同步方式運行和運行。 如果沒有act()您可能會發現測試完成執行useEffect沒有任何意義。

記住所有這些,我們可以測試FilterComponent如何與MoreFiltersComponent一起MoreFiltersComponent (或者更准確地說是與其模擬):

  const wrapper = mount(<FilterComponent data={mockedData} />);
  act(() => {
    wrapper.find(MoreFiltersComponent).invoke('setAmenities')(['a','b']);
  });

  expect(wrapper).toMatchSnapshot(); // should be only those matched filters

PS 我無法回答你如何用useEffectuseState以及shallow()來解決這個任務

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM