首页 > 解决方案 > 使用 Jest 模拟 Firebase onValue 函数

问题描述

我正在尝试onValue()使用 Jest 模拟 Firebase 9.0.0 功能。主组件使用该onValue()函数读取特征并呈现结果。

Acerca.js

import React, { useEffect, useState } from 'react'
import { db, onValue, ref }           from './Firebase'

const Acerca = () => {

    const [feature, setFeature] = useState('')

    useEffect(() => {
        
        let featureRef = ref(db, 'feature')
        
        let unsubscribe = onValue(featureRef, snapshot => { 
            
                
            setFeature(snapshot.val())
            
        })
        
        return () => unsubscribe()
        
    }, [])

    return(
        <>{feature ? <h3>{feature}</h3> : null}</>
    )

}

export default Acerca

Acerca.test.js

import React        from 'react'
import Acerca       from './Acerca'
import { render }   from 'react-dom'
import { act }      from 'react-dom/test-utils'
import { onValue }  from './Firebase'

it("Acerca -> displays title", async () => {
    
    const data = { 
        
        feature: {
            
            title: "New feature"
            
        }
        
    }
    
    const snapshot = { val: () => data }
    
    const firebase = { onValue }
    
    const spy = jest.spyOn(firebase, 'onValue').mockImplementation(() => jest.fn((event, callback) => callback(snapshot)))
    
    await act(async () => {
      
        render(<Acerca/>, container)
      
    })

    expect(container.querySelector('h3').textContent).toBe(data.feature.title)
    
})

问题是没有调用模拟函数(空值而不是虚拟数据)并且测试失败:

在此处输入图像描述

模拟该功能的正确方法是什么onValue()

标签: javascriptreactjsfirebaseunit-testingjestjs

解决方案


Acerca.js

import React, { useEffect, useState } from 'react';
import { db, onValue, ref } from './Firebase';

const Acerca = () => {
  const [feature, setFeature] = useState();

  useEffect(() => {
    let featureRef = ref(db, 'feature');

    let unsubscribe = onValue(featureRef, (snapshot) => {
      setFeature(snapshot.val().feature);
    });

    return () => unsubscribe();
  }, []);

  return <>{feature ? <h3>{feature.title}</h3> : null}</>;
};

export default Acerca;

Firebase.js

// simulate real firebase module
export function onValue(ref, callback) {
  const snapshot = {
    val() {
      return 'real implementation';
    },
  };
  callback(snapshot);
  return function unsubscribe() {};
}

export function ref(db, name) {
  return {};
}

export const db = {};

Acerca.test.js

import React from 'react';
import { render } from 'react-dom';
import { act } from 'react-dom/test-utils';
import { onValue } from './Firebase';
import Acerca from './Acerca';

jest.mock('./Firebase');

describe('Acerca', () => {
  test('should pass', async () => {
    const data = { feature: { title: 'New feature' } };
    const snapshot = { val: () => data };
    onValue.mockImplementation((ref, callback) => {
      callback(snapshot);
      return jest.fn();
    });
    const container = document.createElement('div');
    await act(async () => {
      render(<Acerca />, container);
    });
    expect(container.querySelector('h3').textContent).toBe(data.feature.title);
  });
});

测试结果:

 PASS  examples/69324329/Acerca.test.js (8.029 s)
  Acerca
    ✓ should pass (20 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |      70 |      100 |    37.5 |   73.68 |                   
 Acerca.js   |   91.67 |      100 |      75 |     100 |                   
 Firebase.js |    37.5 |      100 |       0 |    37.5 | 2-8,12            
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.556 s

推荐阅读