Teste a ação firebase do componente da classe com Jest

Oct 24 2020

Sou um novato em brincar e tento testar uma inserção de firebase com o jest. No caso de eu ter um componente de classe chamado Orderse conectar o método de ação firebase como um prop chamado fetchOrders. Aqui está minha configuração,

  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);
         });
     });
    

Então eu executo o caso de teste, aqui está o resultado

(nó: 24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: Nenhum aplicativo Firebase '[DEFAULT]' foi criado - chame Firebase App.initializeApp () (app / no-app). (nó: 24100) UnhandledPromiseRejectionWarning: Rejeição de promessa não tratada. Esse erro foi originado ao lançar dentro de uma função assíncrona sem um bloco catch ou ao rejeitar uma promessa que não foi tratada com .catch (). Para terminar o processo de nó na rejeição de promessa não tratada, use o sinalizador CLI --unhandled-rejections=strict(consultehttps://nodejs.org/api/cli.html#cli_unhandled_rejections_mode) (id de rejeição: 1) (nó: 24100) [DEP0018] Aviso de descontinuação: Rejeições de promessa não tratadas foram descontinuadas. No futuro, as rejeições de promessa que não são tratadas encerrarão o processo Node.js com um código de saída diferente de zero. (nó: 24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: Nenhum aplicativo Firebase '[DEFAULT]' foi criado

  • chame Firebase App.initializeApp () (app / no-app). (nó: 24100) UnhandledPromiseRejectionWarning: Rejeição de promessa não tratada. Esse erro foi originado ao lançar dentro de uma função assíncrona sem um bloco catch ou ao rejeitar uma promessa que não foi tratada com .catch (). Para terminar o processo de nó na rejeição de promessa não tratada, use o sinalizador CLI --unhandled-rejections=strict(consultehttps://nodejs.org/api/cli.html#cli_unhandled_rejections_mode) (id de rejeição: 3) (nó: 24100) UnhandledPromiseRejectionWarning: FirebaseError: Firebase: Nenhum aplicativo Firebase '[DEFAULT]' foi criado
  • chame Firebase App.initializeApp () (app / no-app). (nó: 24100) UnhandledPromiseRejectionWarning: Rejeição de promessa não tratada. Esse erro foi originado ao lançar dentro de uma função assíncrona sem um bloco catch ou ao rejeitar uma promessa que não foi tratada com .catch (). Para terminar o processo de nó na rejeição de promessa não tratada, use o sinalizador CLI --unhandled-rejections=strict(consultehttps://nodejs.org/api/cli.html#cli_unhandled_rejections_mode) (id de rejeição: 4) PASSAR src / test / Orders.test.js (8.099s)

Alguma ideia de consertar isso?

Respostas

1 MoshFeu Oct 26 2020 at 16:17

Isso está acontecendo porque em seu ambiente de teste você não inicia o aplicativo firebase (usando initializeApp) e provavelmente não deveria (você não quer se comunicar com o firebase toda vez que executa um teste, especialmente testes de unidade). Este tipo de pergunta também se aplica a outros serviços externos, como seu próprio servidor e APIs externas.

Então, como você pode testar seu aplicativo?

A resposta é simulada - forneça implementações alternativas para esses serviços para o ambiente de testes. Existem alguns tipos de zombaria, depende do que e como você quer zombar. Além disso, às vezes, as ferramentas fornecem seu próprio testkit (que, novamente, fornece uma implementação testável para os métodos que expõe).

Nesse caso, você pode usar o jestmecanismo de simulação para simular a resposta do firebase para que seu aplicativo "não saiba" que recebeu os dados de outro recurso e aja como deveria.

Os métodos de jest relevantes são spyOne mockImplementation, e aqui está um exemplo (simplifiquei seu componente):

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(Clique na guia Testes )