I wrote a JavaScript Unit Test today... Spy On An Import Time Dependency
Tags: javascript, unittest, jest, skiplist
Published: 2018-05-28 02:22:54

---
title: I wrote a JavaScript Unit Test today... Spy On An Import Time Dependency
published: True
description: An example of mocking and spying on the initialization of a firebase auth provider.
tags: javascript, unittest, jest, skiplist
---

I am initializing a firebase auth provider within a react application.

# Given
```javascript
// base.js
L01  import firebase from 'firebase';
L02  const config = {
L03      apiKey: process.env.REACT_APP_FIREBASE_KEY,
L04      authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
L05      databaseURL: process.env.REACT_APP_FIREBASE_DATABASE,
L06      projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
L07      storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
L08      messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID
L09  };
L10  if (!firebase.apps.length) {
L11      firebase.initializeApp(config);
L12  }
L13  const auth = firebase.auth();
L14  export {auth}
```

# Task
Add unit tests to fully cover and assert the expected behavior for each line of `base.js`.

---

As a `kata` today, I wanted to hit each line of code within an imported JavaScript file. Mocking system wide imports, such as initializing to a database or api, is fundamental in mastering the units of a system.

As mentioned in my previous [JavaScript unit test article](https://dev.to/dev3l/i-wrote-a-javascript-unit-test-today-mock-import-time-function-1jdo), *Languages allow imports to execute non-encapsulated code procedurally. Side effects within these files alter the state of the running system, such as connecting to a database or api, when the instruction pointer links to the file.* As many units as possible should be able to exist within the system without dependency.

# Test
```javascript
// base.test.js

// harness
describe("Base", () => {
    afterEach(() => {
        jest.resetModules()
    });
    // tests go here
});

```

## **Test #1**: Initialize App *is not* called when Firebase has Apps

- Assert that firebase does not call L11 `firebase.initializeApp(config);` when it already has any existing apps.
- Mock the value of firebase.apps to return a truthy value on L10, `firebase.apps = [1]`.
- Use a spy, to assert that L13 was called only once.
- Use the spy, to assert the return value of its function is the default exported value from `base.js`.

```javascript
test("firebase initializeApp not called if already initialized", () => {
    const firebase = require('firebase');
    jest.mock('firebase');

    firebase.initializeApp = (config) => {
        throw "Should not be hit in test"
    };
    firebase.apps = [1];

    let mockAuth = jest.fn();
    let authReturnValue = 'auth'
    mockAuth.mockReturnValueOnce(authReturnValue)
    firebase.auth = mockAuth;

    let auth = require("./base");

    expect(mockAuth.mock.calls.length).toBe(1);
    expect(auth).toEqual({"auth": authReturnValue})
});
```

**With this test, we execute each line of code, outside of L13.**


## **Test #2**: Initialize App *is* called with Firebase config variables

- Assert that initializeApp is called with the expected environment variables.

```javascript
test("firebase initializeApp called with firebase environment variables", () => {
    const firebase = require('firebase');
    jest.mock('firebase');
    
    // hold on to existing env
    const env = process.env;
    // set mock env variables
    process.env = {
        REACT_APP_FIREBASE_KEY: 'key',
        REACT_APP_FIREBASE_DOMAIN: 'domain',
        REACT_APP_FIREBASE_DATABASE: 'database',
        REACT_APP_FIREBASE_PROJECT_ID: 'project',
        REACT_APP_FIREBASE_STORAGE_BUCKET: 'bucket',
        REACT_APP_FIREBASE_SENDER_ID: 'sender',
        REACT_APP_EXTRA_KEY: 'extra'
    };

    const expectedConfig = {
        apiKey: 'key',
        authDomain: 'domain',
        databaseURL: 'database',
        projectId: 'project',
        storageBucket: 'bucket',
        messagingSenderId: 'sender'
    };
    
    // spy for initializeApp
    let mockInitializeApp = jest.fn();
    firebase.initializeApp = mockInitializeApp;
    firebase.apps = [];

    require("./base");
    expect(mockInitializeApp.mock.calls[0][0]).toEqual(expectedConfig);

    // restore env
    process.env = env;
});
```

# Conclusion

[Jest](https://facebook.github.io/jest/) continues to surprise me. I found it's [Mock Functions Documentation](https://facebook.github.io/jest/docs/en/mock-functions.html) to be very user friendly! Mocking is always a tricky subject when it comes to unit testing. Be sure to ask questions if you don't get what is going on here!

[Full source](https://github.com/DEV3L/brain-bit-ledger/tree/master/src/modules/firebase)

![unit test coverage](https://thepracticaldev.s3.amazonaws.com/i/6rj61fu37g954cnur08t.png)

---

[![agile2018endorsement](https://www.agilealliance.org/wp-content/uploads/2018/04/Agile_SD2018_600x100_Speaking_FM.jpg)](http://sched.co/EU9g)