DEV Community

Nestor Solalinde
Nestor Solalinde

Posted on • Originally published at 181.126.83.235 on

Javascript Unit Testing – Jest - Course Notes

Jest Traversy Tutorial

Local folder:
E:\NMS\CURSOS\SMALL-TUTORIALS\jest-tutorial-traversy

Youtube tutorial link:
https://www.youtube.com/watch?v=7r4xVDI2vho

Github link of tutorial:
https://github.com/bradtraversy/jest_testing_basics

Installation

npm init -y
npm i -D jest
# for systax highlighting in vscode (type adquisition)
# https://stackoverflow.com/questions/42024526/vscode-automatic-type-acquisition-for-jest
npm install -D @types/jest
Enter fullscreen mode Exit fullscreen mode

In package.json

  "scripts": {
    "test": "jest"
  },
Enter fullscreen mode Exit fullscreen mode

How to run tests

npm test
Enter fullscreen mode Exit fullscreen mode

Example functions.test.js

const functions = require('./functions');

// to run functions before of after
// beforeEach(() => initDatabase());
// afterEach(() => closeDatabase());
// beforeAll(() => initDatabase());
// afterAll(() => closeDatabase());

//toBe
test('Adds 2 + 2 = 4', ()=> {
    expect(functions.add(2,2)).toBe(4);
});

//to not.toBe
test('Adds 2 + 2 not equal to 5', ()=> {
    expect(functions.add(2,2)).not.toBe(5);
});

// toBeFalsy
test('is 0 falsy?', ()=> {
    expect(0).toBeFalsy();
}); //Answer is yes

// toEqual
test('User should be Brad Traversy object', () => {
    expect(functions.createUser()).toEqual({
      firstName: 'Brad',
      lastName: 'Traversy'
    });
  });

// Async Await
test('User fetched name should be Leanne Graham', async () => {
    // expect.assertions(1);
    const data = await functions.fetchUser();
    expect(data.name).toEqual('Leanne Graham');
  });

// CHECK FOR TRUTHY & FALSY VALUES
// toBeEqual
// toBeNull matches only null
// toBeUndefined matches only undefined
// toBeDefined is the opposite of toBeUndefined
// toBeTruthy matches anything that an if statement treats as true
// toBeFalsy matches anything that an if statement treats as false
Enter fullscreen mode Exit fullscreen mode

Example functions.js

const axios = require('axios');

const functions = {
  add: (num1, num2) => num1 + num2,
  isNull: () => null,
  checkValue: x => x,
  createUser: () => {
    const user = { firstName: 'Brad' };
    user['lastName'] = 'Traversy';
    return user;
  },
  fetchUser: () =>
    axios
      .get('https://jsonplaceholder.typicode.com/users/1')
      .then(res => res.data)
      .catch(err => 'error')
};

module.exports = functions;
Enter fullscreen mode Exit fullscreen mode

Mocks and async functions

In order of getting testing results that are independent of the API's we use, we can use mocks.

For that, we can create a __mocks__ folder that we can call using jest.mock('./mymodule').

An entire module is substituted. When using global package such as axios. You dont need to call jest.mock('axios'), it is done automatically if you place the axios.js file in the __mocks__ folder.

__mocks__/async.js:

const fetchUser = () => {
  console.log("Using mock function for fetchData")
    return Promise.resolve({name:"Leanne Graham"});
  }
exports.fetchUser = fetchUser;
Enter fullscreen mode Exit fullscreen mode

async.js

const axios = require('axios');

const fetchUser = () =>{
  console.log('Fetching data with axios...')
    return axios
      .get('https://jsonplaceholder.typicode.com/users/1')
      .then(res => res.data)
      .catch(err => 'error')
    }
exports.fetchUser = fetchUser;
Enter fullscreen mode Exit fullscreen mode

async.test.js

jest.mock('./async')
const {fetchUser} = require('./async');

// Async Await using mock
test('User fetched name should be Leanne Graham', async () => {
  const data = await fetchUser();
    expect(data.name).toEqual('Leanne Graham');
  });
Enter fullscreen mode Exit fullscreen mode

More code in: https://github.com/academind/js-testing-introduction/tree/async-code

Firebase Functions Unit Testing with Jest

Youtube video:
https://www.youtube.com/watch?v=8IoCPZJ-zwA

Github example:
https://github.com/ssagga/firebase-emulators-testing-with-jest/tree/master/functions

Documentation:
https://firebase.google.com/docs/rules/unit-tests

Local example:
E:\NMS\PROGRAMACION_NMS\CENTRALHUB\CH_SCRAPER\APPSEARCH_FIRESTORE_FUNCTIONS\functions

Example Folder Structure:

myapp
├── node_modules
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── functions
│ ├── node_modules
│ ├── index.js
│ ├── jest.config.js
│ ├── jsconfig.json
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── tests
│ │ │ └── mymodule.test.js
│ │ ├── change_appsearch.js
│ │ ├── change_firestore.js
│ │ ├── testCollectionTriggers.js
│ │ └── testing.js
│ └── ui-debug.log
├── package-lock.json
├── package.json
└── ui-debug.log

To run:

#in one terminal
firebase emulators:start
#in a different terminal
cd functions
jest
Enter fullscreen mode Exit fullscreen mode

functions/jest.config.js

module.exports = {
    testRegex: 'src(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
    testPathIgnorePatterns: ['lib/', 'node_modules/'],
    moduleFileExtensions: ['js','ts','tsx','jsx','json','node'],
    testEnvironment: 'node',
    rootDir: 'src'
}
Enter fullscreen mode Exit fullscreen mode

functions/jsconfig.json

{
    "typeAcquisition": {
        "include": [
            "jest"
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

To run tests sequentially instead of in parallel use jest --runInBand. It is necessary when running firestore test to prevent one test to influence the other.
functions/package.json

  "scripts": {
    ...
    "test": "jest '\\__tests__\\.*.test.js' --runInBand"
  },
Enter fullscreen mode Exit fullscreen mode

functions/src/__tests__/mymodule.test.js

const firebase = require('@firebase/testing')
const admin = require('firebase-admin')

test('Should be 1', ()=> {
    expect(1).toBe(1);
})

const projectId = "rich-meridian-274818"
process.env.GCLOUD_PROJECT = projectId
process.env.FIRESTORE_EMULATOR_HOST = "localhost:8080";
let app = admin.initializeApp({projectId})
let db = firebase.firestore(app)

beforeAll(async ()=>{
    await firebase.clearFirestoreData({projectId});
})

// When Document written to '/TestCollection/{DocumentId}' , trigger function to copy it to '/Copies/{DocumentId}
test("Expect to find a copy in 'Copies' Collection", async ()=>{
    const testDoc = {
        name: 'Samer',
        age: 21,
        city: 'Riyadh'
    }
    const ref = db.collection('TestCollection').doc()
    await ref.set(testDoc)
    const copyId = ref.id
    const copyRef = db.collection('Copies').doc(copyId)
    await new Promise((r)=>setTimeout(r, 3000))
    const copyDoc = await copyRef.get()
    expect(copyDoc.data()).toStrictEqual(testDoc)
})
Enter fullscreen mode Exit fullscreen mode

onCreate function:

const functions = require('firebase-functions');
const admin = require("firebase-admin")

// admin.initializeApp(); //initialized in index.js
const db = admin.firestore()

exports.onCreate = functions.firestore.document('TestCollection/{docId}').onCreate(async (snapshot)=>{
    const data = snapshot.data()
    const docId = snapshot.id
    const copyRef = db.collection('Copies').doc(docId)
    await copyRef.set(data)
})
Enter fullscreen mode Exit fullscreen mode

Top comments (0)