Mock up a single imported function in Firebase

Today, I needed to mock up some of the new Firebase 9.0.0 functions to pass the tests that I've been building over the past years. 💣

I've been struggling when mocking up the new onValue() function. To put things in context, the onValue() function replaces the old on() listener. Thus, the old test looked like this:

javascript
import firebase from '../Functions/Firebase'

// I omit imported components for clarity
// Basically, the component to test reads the title from Firebase 
// using an on()listener and renders the text on a <h3></h3> tag

it("Acerca -> displays feature", async () => {
    
    const data = { 
        
        feature: {
            
            title: "New feature"

        }
        
    }
    
    const snapshot = { val: () => data }
    
    jest.spyOn(firebase, 'database').mockImplementation(() => ({
        
        ref: jest.fn().mockReturnThis(),
        on:  jest.fn((event, callback) => callback(snapshot)),
        off: jest.fn().mockReturnThis()
        
    }));

    await act(async () => {
      
        render(<Acerca/>, container)
      
    });
    
    expect(container.querySelector('h3').textContent).toBe(data.feature.title)
    
})

Mocking up onValue()

At this point, I needed to replace the whole Firebase library by importing a single onValue() function.

My first approach was:

javascript
import { onValue } from './Firebase'

it("Acerca -> displays feature", async () => {
    
    // This approach doesn't work because the spyOn function requires a module as a first parameter    
    jest.spyOn(/* ... */, 'onValue').mockImplementation(() => /* ... */ )
    
})

The second approach was to import all the functions on an object called firebase:

javascript
import * as firebase from './Firebase'

it("Acerca -> displays feature", async () => {
    
    // Even if it doesn't look like an optimal solution, it seems feasible, but it doesn't work...
    // TypeError: cannot redefine property 
    jest.spyOn(firebase, 'onValue').mockImplementation(() => /* ... */ )
    
})

At this point, I was blocked, but thanks to Chak Shun Yu and slideshowp2 I've arrived at the final solution. The idea here is to mock up the entire module and to modify a specific function:

javascript
import { onValue } from './Firebase'

jest.mock('./Firebase')

it("Acerca -> displays feature", async () => {
    
    const data = { 
        
        feature: {
            
            title: "New feature"

        }
        
    }
    
    const snapshot = { val: () => data }
    
    onValue.mockImplementation((ref, callback) => {

        callback(snapshot)
    
        return jest.fn()
    
    })

    await act(async () => {
      
        render(<Acerca/>, container)
      
    });
    
    expect(container.querySelector('h3').textContent).toBe(data.feature.title)
    
})

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.