1

I have, I think, a simple case. I am using a function in my component that is imported from a different file. I've mocked the function as shown below. I've also tried adding a mock implementation, but that didn't work either. If I put a print statement in my original function, that appears when I run the test. If I understand correctly, a mock should replace the original.

I've tried to make this as straightforward as possible, so I'm not sure what I'm missing.

test file:

import AddStoryModal from './AddStoryModal';
import addStory from '../api/add_story';

jest.mock('../api/add_story');

describe('Story tests', () => {

  it('makes an API call to add a story', () => {
    render(<AddStoryModal />);

    const submitButton = screen.getByTestId('add-submit');  
    fireEvent.click(submitButton);
    expect(addStory).toHaveBeenCalled();
  });
});

result:

 expect(jest.fn()).toBeCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

component being tested:

...
import addStory from '../api/add_story';

function AddStoryModal(){

  const onFinish = (values) => {
    addStory();
  };

  return (
    <>
      <Button type="primary" data-testid="add-story" onClick={showModal}>
        Create New +
      </Button>

      <Modal title="Add New Story ..." visible={visible} data-testid='add-story-modal' onCancel={handleCancel} footer={null}>
        <Form form={form} layout="vertical" onFinish={onFinish}>
          ...
          <Form.Item>
            <Button type="primary" data-testid="add-submit" htmlType="submit">
              Submit
            </Button>
          </Form.Item>
        </Form>
      </Modal>
    </>
  );
}

1 Answer 1

1

This is a nice example of how writing tests can make your production code better. Yes, you could continue with using jest.mock(*module*) and work out what's going wrong, but why?

Have a look at what AddStoryModal is actually doing. It's very coupled to calling the API function, which in turn is what is making testing awkward.

If you pass the onFinish implementation function as a prop you immediately get some great wins:

  • Less code in AddStoryModal
  • It becomes more versatile (and increases the chances of being reused)
  • It becomes easier to test

AddStoryModal:

function AddStoryModal(props) {
  return (
   <>
  ...
        <Form form={form} layout="vertical" onFinish={props.onFinish}>
  ...
    </>
  );
}

Now it knows nothing about ../api/add_story

And your test becomes:

import AddStoryModal from './AddStoryModal';

describe('Story tests', () => {
  it('makes an API call to add a story', () => {
    const finishCallback = jest.fn();

    render(<AddStoryModal onFinish={finishCallback} />);

    const submitButton = screen.getByTestId('add-submit');
    fireEvent.click(submitButton);
    expect(finishCallback).toHaveBeenCalled();
  });
});

Super-clean test, easy to understand, and a better overall fit for the React way.

In your production code, whatever page was using <AddStoryModal /> now just uses <AddStoryModal onFinish={addStory} />

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @millhouse. Did the refactoring you suggested, and created a simplified component, but even so, I get the same failure that the mock function is never called.
OK well then it sounds like you've found a bug then! FYI I tested this by just creating a simple button in AddStoryModal and assigned onClick={props.onFinish} - and that worked. So I'd go looking at how your Form fires its onFinish callback - is there something not right there, or is there form validation that is running when the submit button is pressed, that results in the onFinish callback not firing?
Good thought. I'll look into that. It is odd that if I put a console.log in addStory, it does run. In some world, this is onFinish is succeeding. I'll post if/when I sort it out. Thanks.
I think you're right. I'm treating this like a behavioral test, but the event that I want to happen is actually a submit not a click. Thanks for your help!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.