0

Let's say I have this simple React (TypeScript) component:

function Header(): JSX.Element {
   const initialItemIndex = useInitialItemIndex();
   const [currentItem, setCurrentItem] = useState(initialItemIndex);

   function onChangeFunc(event: Event, itemNum: number): void {
       setCurrentItem(itemNum);
   }

   return (
      <div>
         <Tabs value={currentTab} onChange={onChangeFunc}>
            <Tab label="A" />
            <Tab label="B" />
         </Tabs>
      </div>
   );
}

I want to test (Jest, TypeScript) that the Tabs.onChange is calling the onChangeFunc. How should I test it? Something like:

const wrapper = shallow(<Header />);
const spyFunc = jest.spyOn(Header, 'onChangeFunc');
wrapper.find('Tabs').simulate('change');
expect(spyFunc).toBeCalled();

I'm getting error, about the second parameter in the spyOn:

TS2345: Argument of type '"onChangeFunc"' is not assignable to parameter of type 'never'.

What am I doing wrong?

user636312
  • 143
  • 1
  • 4
  • 15
  • 1. Because it's not a property of the `Header` function, it's only defined *inside* the function. 2. It's also part of the thing you're testing, so you *shouldn't* spy on it. – jonrsharpe Mar 30 '20 at 09:28

1 Answers1

0

As @jonrsharpe said, the onChangeFunc defined inside the Header SFC, it's private. We can't access it so that we can't spy on it. You should test it by trigger the change event.

E.g.

index.tsx:

import React from 'react';

export const Tabs = ({ onChange, children }) => <select onChange={onChange}>{children}</select>;
const Tab = ({ label }) => <option value={label}>{label}</option>;

export function Header(): JSX.Element {
  function onChangeFunc(event: Event, tab: number): void {
    console.log('onchange');
  }

  return (
    <div>
      <Tabs onChange={onChangeFunc}>
        <Tab label="A" />
        <Tab label="B" />
      </Tabs>
    </div>
  );
}

index.test.tsx:

import { Header } from './';
import React from 'react';
import { mount } from 'enzyme';

describe('60927553', () => {
  it('should pass', () => {
    const logSpy = jest.spyOn(console, 'log');
    const wrapper = mount(<Header></Header>);
    const mEvent = {};
    wrapper.find('select').simulate('change', mEvent);
    expect(logSpy).toBeCalledWith('onchange');
  });
});

unit test results with 100% coverage:

 PASS  stackoverflow/60927553/index.test.tsx (7.617s)
  60927553
    ✓ should pass (57ms)

  console.log node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866
    onchange

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 index.tsx |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.86s, estimated 9s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Thanks for your replay, but this means that I'll had code just to support the tests ("console.log")? I don't think it's good practice. – user636312 Mar 31 '20 at 10:10
  • @user636312 It's just a demo. You need to assert that some methods which can be mocked or spied in the private function have to been called. – Lin Du Mar 31 '20 at 11:05
  • Can you, please, add example to how to mock? – user636312 Apr 01 '20 at 06:36
  • @user636312 Please edit your `onChangeFunc` function, give the implementation details – Lin Du Apr 01 '20 at 06:37