How to verify console.log was called in componentDidMount using Jest and Enzyme?

jest test console.log output
enzyme mount
enzyme render
jest mock console
jest console log
enzyme debug

I'm trying test for cases when my axios call does not get an HTTP response of 200. When axios does not get a successful response, it throws an error. I want to verify that console.log gets called twice in this case.

Here's a snippet of the class I'm testing:

class App extends React.Component {
    ...  
    async componentDidMount() {
        let url = "/api/info/tmp"
        try {
            let response = await axios.get(url);
            ...do stuff
            this.setState(...);
        } catch (e) {
            console.log("Could not get " + url);
            console.log(e);
        }
    }
    ...
}

And here's a snippet of my jest test

let mockAxios = new MockAdapter(axios);
...
describe("App - componentDidMount() behavior test", () => {
    beforeEach(() => {
        app = shallow(<App />);
    })

    afterEach(() => {
        app = undefined;
        mockAxios.reset();
    });
    ...
    describe("Get " + url + " HTTP response status is not 200", () => {
        beforeAll(() => {
            mockAxios.onGet(url).reply(302, mockData);
        });
        it("Does not set state regardless of response body", () => {
            console.log = jest.fn();
            const state = app.state();
            expect(console.log).toHaveBeenCalledTimes(2);
            expect(state.solutions).toEqual({});
            expect(state.username).toEqual("");
        });
    });
});

I know the console.log = jest.fn() bit is doing something because the console does not log the fake error anymore when I set it. However, the test fails because Expected mock function to have been called two times, but it was called zero times.

I've tried moving the console.log = jest.fn() into the "beforeEach", "beforeAll", and as a global variable.

UPDATE

I am pretty sure it's something to do with all the async that is going on. If I do this:

    it("Does not set state regardless of response body", async () => {
        console.log = jest.fn();
        await app.instance().componentDidMount();
        expect(console.log).toHaveBeenCalledTimes(2);

        const state = app.state();
        expect(state.solutions).toEqual({});
        expect(state.username).toEqual("");
    });

Then the test still fails but my reason changed: Expected mock function to have been called two times, but it was called four times. Now I just got to figure out why it was called four times not twice.

UPDATE 2

I figured out why console.log was being called 4 times! Now I just need to figure out how I should refactor my tests. If I comment out my jest mock, and even the whole unit test

    it("Does not set state regardless of response body", async () => {
        //const state = app.state();
        //expect(state.solutions).toEqual({});
        //expect(state.username).toEqual("");
        //expect(console.log).toHaveBeenCalledTimes(2);
    });

Then I can count in my console that there are already indeed two different console.log calls. shallow(<App />) must be already calling componentDidMount() or something. When I add app.instance().componentDidMount(), I can visually see that it is logging 4 times.

Updated Answer

Since it looks like you already know what you're doing with mocks, perhaps the issue has to do with componentDidMount().

I believe that your call to shallow(<App />) will already call App's componentDidMount() one time (which means your console.log will get called twice there).

Then, you subsequently call app.instance().componentDidMount() - that is, you call componentDidMount() again (which means your console.log will get called twice there again).

So, total... 4 calls to console.log.

Hope that points you in the right direction...

Original Answer

Actually, your question looks quite similar to [this StackOverFlow question on how to "How to mock console when it is used by a third-party library?"

You can use Jest mock functions to spyOn the global.console object.

For example, your test may look like this:

// Setup jest to spy on the console
const consoleSpy = jest.spyOn(global.console, 'log')

describe('App - componentDidMount() behavior test', () => {
  beforeEach(() => {
    jest.resetAllMocks()  // reset your consoleSpy state back to initial
    app = shallow(<App />)
  })

  ...

      it('Does not set state regardless of response body', () => {
        const spy = jest.spyOn(global.console, 'log')
        const state = app.state()
        expect(consoleSpy).toHaveBeenCalledTimes(2)
        expect(state.solutions).toEqual({})
        expect(state.username).toEqual('')
      })
  ...

jest enzyme test method in componentDidMount fails but console , lastUpdated); } } } When loadData() is called I have 3 more console.log that are This tells me that it is actually working however something with Jest/Enzyme� I have a container class LoadingIndicator connected using redux. I am trying to test if the componentDidMount calls the ensureTimer function using Enzyme and Jest.

Ideally, you'd move your API call outside of componentDidMount and into its own class method. Thay way it can be manually invoked from a lifecycle method or from an event callback. Also, you should anticipate the response to affect your UI state in some fashion (example: displaying a message to the user that the request failed and to try again).

The following example can be done with .then/.catch instead of async/await. Either way, you're working with Promises that are asynchronous and therefore they need asynchronous tests.

Note: The below assumes disableLifecycleMethods is true in the enzyme adapter. Also, just testing state changes (or a console.log) is a bit superfluous; instead, you would test if a component is rendered based upon the current state.

Working example: https://codesandbox.io/s/939w229l9r (includes both end to end and integration tests --- you can run the tests by clicking on the Tests tab located near the bottom left of the sandbox)


App.js (this will be a container that holds all relevant state and disperses it to its children as needed)

import React, { Component } from 'react';

class App extends Component {
  state = = {
    error: "", 
    isLoading: true,
    solutions: {}, 
    username: ""
  };

  componentDidMount() {
    this.fetchData("/api/info/tmp");
  }

  fetchData = async (url) => {
    try {
      const res = await axios.get(url);

      ...do stuff

      this.setState({ 
        error: "", 
        isLoading: false, 
        solutions: res.data.solutions, 
        username: res.data.username 
      });
    } catch (err) {
        this.setState({ 
          error: err, 
          isLoading: false, 
          solutions: {}, 
          username: "" 
        });
    }
  }

  render() { ... }
}

App.test.js (this assumes you'd want an end to end test)

import { shallow } from 'enzyme';
import App from './App';

const timeout = () =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, 2000);
  });

const initialState = {
  error: "", 
  isLoading: true,
  solutions: {}, 
  username: ""
};

describe("App", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<App />);
    wrapper.setState({ ...initialState });
  });

  afterAll(() => {
     wrapper.unmount();
  });

  it("sets data to state based upon successful API call", async () => { 
   wrapper.instance().fetchData("/api/info/tmp");
   await timeout();

   wrapper.update();
   expect(wrapper.state('isLoading')).toBeFalsy();
   expect(wrapper.state('solutions')).toEqual({ somedata });
   expect(wrapper.state('username')).toEqual("Some User");
  });

  it("displays an error upon unsuccessful API call", async () => { 
   wrapper.instance().fetchData("/api/bad/url");
   await timeout();

   wrapper.update();
   expect(wrapper.state('isLoading')).toBeFalsy();
   expect(wrapper.state('solutions')).toEqual({});
   expect(wrapper.state('username')).toEqual("");
   expect(wrapper.state('error')).toEqual("No data found.");
  });
});

App.test.js (this assumes you'd want an integration test)

import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import React from "react";
import { shallow } from "enzyme";
import App from "../App";

const solutions = [{ ... }, { ... }];
const username = "Some User"

const mockAxios = new MockAdapter(axios);

const initialState = {
  error: "", 
  isLoading: true,
  solutions: {}, 
  username: ""
};

describe("App", () => { 
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<App />);
    wrapper.setState({ ...initialState });
  });

  afterEach(() => {
    mock.reset();
  });

  afterAll(() => {
    mock.restore();
    wrapper.unmount();
  });

  it("displays an error upon unsuccessful API call", async () => { 
    try {
       mockAxios.onGet("/users").networkErrorOnce();

       await axios.get("users");

     } catch (err) {
       const error = err.toString();
       wrapper.setState({ 
         error, 
         isLoading: false,
         solutions: {}, 
         username: ""
       });

       wrapper.update();
       expect(wrapper.state('isLoading')).toBeEqual(error);
       expect(wrapper.state('isLoading')).toBeFalsy();
       expect(wrapper.state('solutions')).toEqual({});
       expect(wrapper.state('username')).toEqual("");
     } 
  });

  it("sets data to state based upon successful API call", async () => {
    try {
      mockAxios.onGet("/users").reply(200, { solutions, username });

      const res = await axios.get("users");

      wrapper.setState({ 
        error: "", 
        isLoading: true,
        solutions: res.data.solutions, 
        username: res.data.username
      });

      wrapper.update();
      expect(wrapper.state('isLoading')).toBeFalsy();
      expect(wrapper.state('solutions')).toEqual(solutions);
      expect(wrapper.state('username')).toEqual(username);
    } catch (e) {
        console.log(e);
    } 
  });
});

Jest/Enzyme, My goal is to check if Linking.canOpenUrl ever fires off. Exmaple The function inside the component looks like this, onPressLink() { console.log('HEY THIS� I am using the @quasar/testing harness for Jest and everything is fine except that I can not use console.log() in tests, instead the console shows the following error: Do not use console.log() in production It even persists when I go to .eslintrc.js and d

I figured it out! Kind of... I am not certain why it works like this, but setting the mock in the actual "it" did not work. The solution was making a beforeEach and afterEach

describe("Get " + url + " HTTP response status is not 200", () => {
    beforeAll(() => {
        mockAxios.onGet(url).reply(302, mockData);
    });
    beforeEach(() => {
        console.log = jest.fn();
    });

    afterEach(() => {
        jest.resetAllMocks();
    });
    it("Does not set state regardless of response body", async () => {
        const state = app.state();
        expect(state.solutions).toEqual({});
        expect(state.username).toEqual("");
        expect(console.log).toHaveBeenCalledTimes(2);
    });
});

Testing component with async componentDidMount � Issue #1587 , Testing component with async componentDidMount #1587 Provide an API to flush the Promise resolution queue facebook/jest#2157 import React from ' react'; import { shallow } from 'enzyme'; import MFASection from '. mounts - 2 ( 4ms) console.log src/stackoverflow/58648463-todo/index.tsx:13� Enzyme is an open source JavaScript testing utility by Airbnb that makes it fun and easy to write tests for React. In this article, we will be going through writing tests for React using Enzyme and Jest. To get started, you will need to familiarize yo

How to test a React component that sets its state in , sets its state in componentDidMount with fetch, and how to mock it, in Jest. Fetch.test.js import {shallow, mount, render} from 'enzyme' console.log(` fetchResponseJson failed:`, e) By returning the promise (a call to an async function) we can await componentDidMount Force update to sync component with state. How to test a React component that sets its state in componentDidMount with fetch, and how to mock it, in Jest - Fetch.test.js

Testing with Jest and Enzyme in React — Part 5 (Dive with Jest and , In this tutorial let us dive more into testing with Jest and Enzyme by writing First, create a new file named Form.test.js inside the testing-demo-app/test folder with the console.log('componentDidMount method is called'); }; Jest runs a test; Jest outputs the console.log statements (don't blink here) Jest scrolls back up an arbitrary number of lines, which sometimes covers all the console.log lines, sometimes some and sometimes all. Jest runs the next test (console.log lines from previous test disappear). jest v18.1.0

Test Renderer – React, import TestRenderer from 'react-test-renderer'; function Link(props) { return <a console.log(testRenderer. You can use Jest's snapshot testing feature to automatically save a copy of the JSON tree to a file and check in your tests null; } componentDidMount() { this.input.focus(); } render() { return <input type="text" ref={el� Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with `new`, and allowing test-time configuration of return values.

Comments
  • Unfortunately this is not it. I did make an update though! It's something to do with all the asynchronous stuff going on.
  • @Pickle_Jr Are you making sure that you are resetting all of your mocks with each run? If you're getting Expected mock function to have been called two times, but it was called four times. then is it possible that other test runs are also leading to console.log getting called? Sorry... just a shot in the dark...
  • No worries, I appreciate it! I think it's something shallow(<App />) did. I updated my post again.