Skip to content

Latest commit

Β 

History

History
177 lines (118 loc) Β· 6.71 KB

File metadata and controls

177 lines (118 loc) Β· 6.71 KB
title act

actλŠ” ν…ŒμŠ€νŠΈ 헬퍼Helper둜, λŒ€κΈ° 쀑인 React μ—…λ°μ΄νŠΈλ₯Ό λͺ¨λ‘ μ μš©ν•œ λ’€ 단언Assertν•  수 있게 도움을 μ€λ‹ˆλ‹€.

await act(async actFn)

μ»΄ν¬λ„ŒνŠΈλ₯Ό 단언Assertionν•  수 μžˆλ„λ‘ μ€€λΉ„ν•˜λ €λ©΄ await act() 호좜 μ•ˆμ— μ»΄ν¬λ„ŒνŠΈλ₯Ό λ Œλ”λ§ν•˜κ³  μ—…λ°μ΄νŠΈν•˜λŠ” μ½”λ“œλ₯Ό κ°μ‹Έμ„Έμš”. μ΄λ ‡κ²Œ ν•˜λ©΄ ν…ŒμŠ€νŠΈκ°€ λΈŒλΌμš°μ €μ—μ„œ μž‘λ™ν•˜λŠ” μ‹€μ œ React 방식과 더 μœ μ‚¬ν•˜κ²Œ μ‹€ν–‰λ©λ‹ˆλ‹€.

`act()`λ₯Ό 직접 μ‚¬μš©ν•˜λŠ” 것이 λ‹€μ†Œ μž₯ν™©ν•˜λ‹€κ³  느껴질 수 μžˆμŠ΅λ‹ˆλ‹€. λ°˜λ³΅λ˜λŠ” μ½”λ“œλ₯Ό 쀄이고 μ‹Άλ‹€λ©΄ [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)처럼 λ‚΄λΆ€μ μœΌλ‘œ `act()`둜 감싼 헬퍼λ₯Ό μ œκ³΅ν•˜λŠ” 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 것도 μ’‹μŠ΅λ‹ˆλ‹€.

레퍼런슀 {/reference/}

await act(async actFn) {/await-act-async-actfn/}

UI ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  λ•Œ λ Œλ”λ§, μ‚¬μš©μž 이벀트, 데이터 κ°€μ Έμ˜€κΈ° 등은 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€μ™€μ˜ μƒν˜Έμž‘μš© "λ‹¨μœ„"둜 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. ReactλŠ” act()λΌλŠ” 헬퍼λ₯Ό μ œκ³΅ν•˜λŠ”λ° μ΄λŠ” 이 "λ‹¨μœ„"와 κ΄€λ ¨λœ λͺ¨λ“  μ—…λ°μ΄νŠΈκ°€ DOM에 적용되기 μ „κΉŒμ§€ 단언이 μ‹€ν–‰λ˜μ§€ μ•Šλ„λ‘ 보μž₯ν•΄ μ€λ‹ˆλ‹€.

act λΌλŠ” 이름은 Arrange-Act-Assert νŒ¨ν„΄μ—μ„œ λ”°μ˜¨ κ²ƒμž…λ‹ˆλ‹€.

it ('renders with button disabled', async () => {
  await act(async () => {
    root.render(<TestComponent />)
  });
  expect(container.querySelector('button')).toBeDisabled();
});

actλŠ” await와 async ν•¨μˆ˜μ™€ ν•¨κ»˜ μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€. 동기 버전도 λŒ€λΆ€λΆ„μ˜ 경우 λ™μž‘ν•˜μ§€λ§Œ Reactκ°€ λ‚΄λΆ€μ μœΌλ‘œ μ—…λ°μ΄νŠΈλ₯Ό μ˜ˆμ•½ν•˜λŠ” 방식 λ•Œλ¬Έμ— μ–Έμ œ 동기 버전을 써도 λ˜λŠ”μ§€ μ˜ˆμΈ‘ν•˜κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€.

μ•žμœΌλ‘œ 동기 버전은 더 이상 μ‚¬μš©λ˜μ§€ μ•Šμ„ μ˜ˆμ •μ΄λ©° 제거될 μ˜ˆμ •μž…λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜ {/parameters/}

  • async actFn: ν…ŒμŠ€νŠΈν•  μ»΄ν¬λ„ŒνŠΈλ₯Ό λ Œλ”λ§ν•˜κ±°λ‚˜ μƒν˜Έμž‘μš©μ„ μˆ˜ν–‰ν•˜λŠ” 비동기 ν•¨μˆ˜μž…λ‹ˆλ‹€. actFn λ‚΄λΆ€μ—μ„œ λ°œμƒν•˜λŠ” μ—…λ°μ΄νŠΈλŠ” λ‚΄λΆ€ act 큐에 μΆ”κ°€λ˜λ©° λͺ¨λ‘ λͺ¨μ•„μ„œ DOM에 μ μš©λ©λ‹ˆλ‹€. 비동기 ν•¨μˆ˜μ΄κΈ° λ•Œλ¬Έμ— ReactλŠ” 비동기 경계λ₯Ό λ„˜λŠ” μ½”λ“œλ„ μ‹€ν–‰ν•˜κ³  μ˜ˆμ•½λœ μ—…λ°μ΄νŠΈλ„ ν•¨κ»˜ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

λ°˜ν™˜κ°’ {/returns/}

actλŠ” 아무 값도 λ°˜ν™˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ‚¬μš©λ²• {/usage/}

μ»΄ν¬λ„ŒνŠΈλ₯Ό ν…ŒμŠ€νŠΈν•  λ•Œ actλ₯Ό μ‚¬μš©ν•˜λ©΄ 좜λ ₯ 결과에 λŒ€ν•œ 단언을 더 μ•ˆμ „ν•˜κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œλ‘œ CounterλΌλŠ” μ»΄ν¬λ„ŒνŠΈκ°€ μžˆλ‹€κ³  κ°€μ •ν•˜κ³  μ•„λž˜ μ‚¬μš© μ˜ˆμ‹œλŠ” 이λ₯Ό ν…ŒμŠ€νŠΈν•˜λŠ” 방법을 λ³΄μ—¬μ€λ‹ˆλ‹€.

function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(prev => prev + 1);
  }

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  )
}

ν…ŒμŠ€νŠΈμ—μ„œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ Œλ”λ§ν•˜λŠ” 방법 {/rendering-components-in-tests/}

μ»΄ν¬λ„ŒνŠΈμ˜ λ Œλ”λ§ κ²°κ³Όλ₯Ό ν…ŒμŠ€νŠΈν•˜λ €λ©΄ λ Œλ”λ§ μ½”λ“œλ₯Ό act()둜 감싸야 ν•©λ‹ˆλ‹€.

import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';

it('can render and update a counter', async () => {
  container = document.createElement('div');
  document.body.appendChild(container);

  // βœ… μ»΄ν¬λ„ŒνŠΈλ₯Ό act() μ•ˆμ—μ„œ λ Œλ”λ§ν•©λ‹ˆλ‹€.
  await act(() => {
    ReactDOMClient.createRoot(container).render(<Counter />);
  });

  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');
});

μœ„ μ˜ˆμ‹œμ—μ„œλŠ” μ»¨ν…Œμ΄λ„ˆλ₯Ό λ§Œλ“€κ³  λ¬Έμ„œμ— μΆ”κ°€ν•œ λ’€ Counter μ»΄ν¬λ„ŒνŠΈλ₯Ό act() μ•ˆμ—μ„œ λ Œλ”λ§ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ»΄ν¬λ„ŒνŠΈκ°€ λ Œλ”λ§λ˜κ³  νš¨κ³Όκ°€ 적용된 후에 단언을 μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

actλ₯Ό μ‚¬μš©ν•˜λ©΄ λͺ¨λ“  μ—…λ°μ΄νŠΈκ°€ 적용된 λ’€ 단언을 μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈμ—μ„œ 이벀트 λ””μŠ€νŒ¨μΉ­ν•˜λŠ” 방법 {/dispatching-events-in-tests/}

이벀트λ₯Ό ν…ŒμŠ€νŠΈν•˜λ €λ©΄ 이벀트λ₯Ό act()둜 κ°μ‹Έμ„Έμš”.

import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';

it.only('can render and update a counter', async () => {
  const container = document.createElement('div');
  document.body.appendChild(container);

  await act( async () => {
    ReactDOMClient.createRoot(container).render(<Counter />);
  });

  // βœ… 이벀트 λ””μŠ€νŒ¨μΉ˜λ₯Ό act() μ•ˆμ—μ„œ μ‹€ν–‰ν•©λ‹ˆλ‹€.
  await act(async () => {
    button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  });

  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});

μœ„ μ˜ˆμ‹œμ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈλ₯Ό λ¨Όμ € act둜 감싸 λ Œλ”λ§ν•˜κ³ , 이벀트 λ””μŠ€νŒ¨μΉ˜λ„ act()둜 κ°μŒ‰λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν•΄λ‹Ή 이벀트둜 μΈν•œ λͺ¨λ“  μ—…λ°μ΄νŠΈκ°€ 적용된 λ’€ 단언이 μˆ˜ν–‰λ©λ‹ˆλ‹€.

DOM 이벀트λ₯Ό λ””μŠ€νŒ¨μΉ˜ν•  λ•ŒλŠ” DOM μ»¨ν…Œμ΄λ„ˆκ°€ λ¬Έμ„œμ— μΆ”κ°€λ˜μ–΄ μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. λ°˜λ³΅λ˜λŠ” μ„€μ • μ½”λ“œλ₯Ό 쀄이고 μ‹Άλ‹€λ©΄ React Testing Libraryλ₯Ό μ‚¬μš©ν•˜λŠ” 것도 κ³ λ €ν•΄λ³΄μ„Έμš”.

문제 ν•΄κ²° {/troubleshooting/}

"The current testing environment is not configured to support act(...)" 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 경우 {/error-the-current-testing-environment-is-not-configured-to-support-act/}

actλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ ν…ŒμŠ€νŠΈ ν™˜κ²½μ—μ„œ global.IS_REACT_ACT_ENVIRONMENT=trueλ₯Ό μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. 이 섀정은 actκ°€ μ˜¬λ°”λ₯Έ ν™˜κ²½μ—μ„œλ§Œ μ‚¬μš©λ˜λ„λ‘ 보μž₯ν•©λ‹ˆλ‹€.

이 μ „μ—­ 섀정이 μ—†μœΌλ©΄ λ‹€μŒκ³Ό 같은 였λ₯˜κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.

Warning: The current testing environment is not configured to support act(...)

이 문제λ₯Ό ν•΄κ²°ν•˜λ €λ©΄ React ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ μ „μ—­ μ„€μ • νŒŒμΌμ— λ‹€μŒ μ½”λ“œλ₯Ό μΆ”κ°€ν•˜μ„Έμš”.

global.IS_REACT_ACT_ENVIRONMENT=true

React Testing Library같은 ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬μ—μ„œλŠ” IS_REACT_ACT_ENVIRONMENTκ°€ 이미 μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.