5
votes

I'm a beginner to jest and I try to test a firebase insert with jest. In case I have a class component called Orders and connect the firebase action method as a prop called fetchOrders. Here is my setup,

  1. OrderAction.js

    import { app } from "firebase";
    import {
     DATABASE_COLLECTION_ORDERS,
    } from "../../config";
    
    export const fetchOrders = () => async (dispatch) =>
    {
     const objList = [];
    
     app().firestore().collection(DATABASE_COLLECTION_ORDERS).get()
         .then((snapShot) =>
         {
             snapShot.forEach((doc) =>
             {
                 if (doc.exists)
                 {
                     // get the collection primary id
                     const { id } = doc;
                     // read the data
                     const data = doc.data();
    
                     objList.push({ ...data, id });
                 }
             });
    
             dispatch({
                 type    : ORDER_APPEND,
                 payload : objList,
             });
         })
         .catch((err) =>
         {
             reportError(err);
         });
    };
    
  2. Orders.js

    import React from "react";
    import { fetchOrders, fetchOrderItems } from "../../redux/action/OrderAction";
    
    class Orders extends React.Component{
       async componentDidMount()
       {
          this.props.fetchOrders();
       }
       render()
       {
           return (
                ...content
           )
       }
    }
    
    const mapStateToProps = (state) => ({
       orders   : state.orders,
    });
    
    export default connect(mapStateToProps,
    { 
        fetchOrders,
    })(Orders);
    
  3. Orders.test.js

     import React from 'react';
     import { configure, mount } from 'enzyme';
     import Adapter from 'enzyme-adapter-react-16';
     import { Provider } from 'react-redux';
     import configEnzyme from "../setupTest";
     import store from "../redux/store";
     import Orders from "../views/basic/Orders";
    
     describe('Test case for testing orders', () =>
     {
         // configure the jtest
         configure({ adapter: new Adapter() });
    
         // mount login component to the wrapper
         let wrapper;
    
         const originalWarn = console.warn;
    
         // console.warn = jest.fn();
    
         // disable the console warnings
         beforeEach(() =>
         {
             // jest.spyOn(console, 'warn').mockImplementation(() => {});
             // jest.spyOn(console, 'error').mockImplementation(() => {});
    
             wrapper = mount(
                 <Provider store={store}>
                     <Orders />
                 </Provider>,
             );
         });
    
         // test case fetching data
         it('check input data', () =>
         {
             const componentInstance = wrapper.find('Orders').instance();
    
             componentInstance.props.fetchOrders();
    
             // wait for 2 seconds for redux store update
             jest.useFakeTimers();
             setTimeout(() =>
             {
                wrapper.update();
    
                expect(componentInstance.props.orderHeader).not.toBeNull();
             }, 2000);
         });
     });
    

Then I run the test case, here is the result

(node:24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app). (node:24100) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To t erminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) (node:24100) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. (node:24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created

  • call Firebase App.initializeApp() (app/no-app). (node:24100) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To t erminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3) (node:24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created
  • call Firebase App.initializeApp() (app/no-app). (node:24100) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To t erminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 4) PASS src/test/Orders.test.js (8.099s)

Any idea to fix this?

1

1 Answers

2
votes

It's happening because in your test environment you're not initiate the firebase app (using initializeApp) and you probably shouldn't (you don't want to communicate with firebase every time you run a test, especially not unit tests). This kind question also applies to other external services like your own server and external APIs.

So how can you test your app?

The answer is mocking - supply an alternative implementations for those services to the tests environment. There are some kind of mocking, depends on what and how you want to mock. Also, sometimes, the tools supply their own testkit (which, again, supply a testable implementation for the methods it expose).

In this case, you can use jest mocking mechanizm to mock the response from firebase so your app "doesn't know" it received the data from other resource, and will act like it should.

The relevant jest methods are spyOn and mockImplementation, and here is an example (I simplified your component):

App.spec.js

test("mount", async () => {
  const fetchPromise = Promise.resolve([{ name: "order1" }]);
  jest.spyOn(firebase, "app").mockImplementation(() => ({
    firestore: () => ({
      collection: () => ({
        get: () => fetchPromise
      })
    })
  }));
  let wrapper = mount(<App />);
  await fetchPromise;
  wrapper.update();
  expect(wrapper.find("span").text()).toBe("order1");
});

App.js

export default class App extends React.Component {
  state = {
    orders: []
  };

  fetchOrders() {
    try {
      app()
        .firestore()
        .collection(DATABASE_COLLECTION_ORDERS)
        .get()
        .then((snapShot) => {
          this.setState({ orders: snapShot });
        });
    } catch {
      console.log("do nothing");
    }
  }

  componentDidMount() {
    this.fetchOrders();
  }

  render() {
    return (
      <div className="App">
        {this.state.orders.map((order) => (
          <span key={order.name}>{order.name}</span>
        ))}
      </div>
    );
  }
}

https://codesandbox.io/s/enzyme-setup-in-codesandbox-forked-jyij5?file=/src/App.js:133-730 (Click on the Tests tab)