3

I'm following along in this Jest test tutorial on Pluralsight here. And I've written code exactly like the author, but for some reason my test is not passing.

My Pull request to the author's repo: https://github.com/danielstern/isomorphic-react/pull/19

I have a simple React component, it updates it's count state with a async/await call to a service within the componentDidMount.

{this.state.count != -1 ? `${this.state.count} Notifications Awaiting` : 'Loading...'}

EXPECTED

Since I've mocked the NotificationsService, and set count to 42, the test should pass with text inside the component being "42 Notifications Awaiting!"

RESULTS

The text stays stuck as the default Loading...

I've mocked the service correctly, and the count variable is even being logged correctly as 42! However this.state.count is still -1 so instead of displaying: ${this.state.count} Notifications Awaiting it still displays Loading... and thus fails the test.


What I've tried

1) I've tried adding 1000 into the delay.

2) Tried using setTimeout inside the test.

3) Tried jest.useFakeTimers(); and jest.runAllTimers();

However nothing is working, the count inside of the component is stuck at -1 even though count is set to 42. It just seems to me that my test is running before the state is finished being set?

enter image description here

The NotificationsViewser.jsx Component

import React from 'react';
import NotificationsService from '../services/NotificationsService';

export default class componentName extends React.Component {
  constructor(...args) {
    super(...args);

    this.state = {
      count: -1
    }
  }

  async componentDidMount () {
    let { count } = await NotificationsService.GetNotifications();
    console.log('count:', count);
    this.setState({
      count
    });
  }

  componentDidUpdate() {
    console.log('componentDidUpdate:', this.state);
  }

  render() {
    return (
      <div className="mt-3 mb-2">
        <div className="notifications">
          {this.state.count != -1 ? `${this.state.count} Notifications Awaiting` : `Loading...`}
        </div>
      </div>
    )
  }
}

NotificationsService.js

import { delay } from 'redux-saga';

export default {
  async GetNotifications() {
    console.warn("REAL NOTIFICATION SERVICE! CONTACTING APIS!");

    await delay(1000);
    return { count: 42 };
  }
}

mocks: NotificationsService.js

let count = 0;

export default {
  __setCount(_count) {
    count = _count;
  },
  async GetNotifications() {
    console.warn("GOOD JOB! USING MOCK SERVICE");
    return { count };
  }
}

Finally...

The Test

import React from 'react';
import renderer from 'react-test-renderer';
import delay from 'redux-saga';

import NotificationsViewer from '../NotificationsViewer';

jest.mock('../../services/NotificationsService');

const notificationService = require('../../services/NotificationsService').default;

describe('The notification viewer', () => {

  beforeAll(() => {
    notificationService.__setCount(42);
  });

  it('should display the correct number of notifications', async() => {
    const tree = renderer
      .create(
        <NotificationsViewer/>
      );

    await delay();

    const instance = tree.root;
    const component = instance.findByProps({className: `notifications`});
    const text = component.children[0];
    console.log('text is:', text);

    expect(text).toEqual('42 Notifications Awaiting!');
  });
})
1
  • It seems to me that the await delay() is not letting the life cycle methods run first before the expect block is tested. Because the results of console.log('text is:', text); are hit first, then the componentDidMount and componentDidUpdate logs are hit. Commented Jun 18, 2019 at 16:41

3 Answers 3

1

In fact, the real problem was in isomorphic-react/src/components/__tests__/NotificationsViewer.js file. The delay are imported the wrong way and that was causing the error on test.

If importing delay like this: import { delay } from 'redux-saga' fix the problem. =D

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

1 Comment

This is correct, I had the import wrong. Leaving my answer below as a preferred way to handle this however, as not every project will be using redux-saga and my example explains how to use Enzyme to solve this issue :)
1

The problem was that await delay() did not work to let all the React life cycle methods like componentDidMount instantiate / get called.

I had to result to using Enzyme even though the author did not recommend it due to a lot of open bug issues.

Using Enzyme I could ensure that the componentDidMount was called, and thus setting the state of count to 42 with the mocked service.

I also needed to installed the following packages:

"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",

Fixed test

import React from 'react';
import renderer from 'react-test-renderer';
import Adapter from 'enzyme-adapter-react-16';
import { shallow, configure } from 'enzyme';

configure({adapter: new Adapter()});

import NotificationsViewer from './NotificationsViewer';

jest.mock('../services/NotificationsService');

const notificationService = require('../services/NotificationsService').default;

notificationService.default = jest.fn();

describe('The notification viewer', () => {

  beforeAll(() => {
    notificationService.__setCount(42);
  });

  // it('pass', () => {});

  it('should display the correct number of notifications', async() => {
    const tree = renderer.create(<NotificationsViewer />);
    const wrapper = shallow(<NotificationsViewer />);
    const instance = tree.root;

    await wrapper.instance().componentDidMount();

    const component = instance.findByProps({className: `notifications`});
    const text = component.children[0];
    console.log('text is:', text);

    expect(text).toEqual('42 Notifications Awaiting');
  });
})

enter image description here

Comments

0

Maybe your describe also needs to be an async function? await statements need to be declared inside an async scope, no?

2 Comments

Ok so I just tried that but got this warning > Returning a Promise from "describe" is not supported. Tests must be defined synchronously. Returning a value from "describe" will fail the test in a future version of Jest.
Ops, I missed the async on the it block

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.