用 Jest 模拟 Firebase 功能(单元测试)

Posted

技术标签:

【中文标题】用 Jest 模拟 Firebase 功能(单元测试)【英文标题】:Mocking Firebase function with Jest (Unit testing) 【发布时间】:2021-10-13 07:59:03 【问题描述】:

我正在创建单元测试,但我可以找到一种方法来模拟 firebase 函数并在调用它们时指定返回类型。下面我发布了我想要模拟的内容(account.service.ts)以及我当前进行的测试。我想模拟并指定要使用 admin 返回的内容...(也就是设置 resp 对象值)。

以管理员身份从“firebase-admin”导入 *;

account.service.ts

    const resp = admin
        .auth()
        .createUser(
            email: registerDto.email,
            emailVerified: false,
            password: registerDto.password,
            displayName: registerDto.displayName,
            disabled: false,
        )
        .then(
            (userCredential): Account => (
                uid: userCredential.uid,
                email: userCredential.email,
                emailVerified: userCredential.emailVerified,
                displayName: userCredential.displayName,
                message: 'User is successfully registered!',
            ),
        )
        .catch((error) => 
            // eslint-disable-next-line max-len
            throw new HttpException(`$'Bad Request Error creating new user: '$error.message`, HttpStatus.BAD_REQUEST);
        );

account.service.spec.ts

describe('61396089', () => 
        afterEach(() => 
            jest.restoreAllMocks();
        );

        const obj = 
            uid: 'uid',
            email: 'email',
            emailVerified: 'emailVerified',
            displayName: 'displayName',
            message: 'User is successfully registered!',
        ;

        const jestAdminMock = 
            admin: () => (
                auth: () => (
                    createUser: () => (
                        then: () => (
                            catch: () => obj,
                        ),
                    ),
                ),
            ),
        ;

        it('should pass', () => 
            const mockDataTable = 
                admin: jest.fn().mockReturnThis(),
                auth: jest.fn().mockReturnThis(),
                createUser: jest.fn().mockReturnThis(),
                then: jest.fn().mockReturnThis(),
                catch: jest.fn().mockReturnValueOnce(obj),
            ;
            jest.spyOn(jestAdminMock, 'admin').mockImplementationOnce(() => mockDataTable);
            const actual = service.registerUser(registerDTO);
            expect(actual).toBe(obj);
        );
    );
);

【问题讨论】:

admin 对象从何而来? 以管理员身份从 'firebase-admin' 导入 *; 【参考方案1】:

我设法做了一个像这个例子这样的测试

这是函数

import admin from 'firebase-admin'

admin.initializeApp(
  credential: admin.credential.cert(credentials),
)

const createClosure = (admin) => 
  if (!admin) 
    throw new Error(Errors.FIREBASE_ADMIN_SDK_NOT_PROVIDED)
  
  return (data) => 
    if (
      data &&
      !Array.isArray(data) &&
      typeof data === 'object' &&
      Object.keys(data).length > 0
    ) 
      const  firstName, lastName  = data
      const displayName = `$firstName $lastName`
      return admin.auth().createUser( ...data, displayName )
    
    throw new Error(Errors.INVALID_DATA)
  


/*
.....
*/

const create = createClosure(admin)

export  create, createClosure 

这是一个测试示例

import  createClosure  from "../path/to/function"

describe('createClosure', () => 
  it('should be a function', () => 
    expect(typeof createClosure).toBe('function')
  )

  describe('when admin is not provided', () => 
    it('should throw "Firebase Admin SDK not provided"', () => 
      const expected = Errors.FIREBASE_ADMIN_SDK_NOT_PROVIDED
      expect(() => createClosure()).toThrow(expected)
    )
  )
  describe('when admin is provided', () => 
    describe('when data is invalid', () => 
      const createUser = jest.fn()
      const admin = 
        auth: () => (
          createUser,
        ),
      
      const data1 = 123
      const data2 = 'hello'
      const data3 = ['a', 'b', 'c']
      const data4 = 

      it('should throw "Invalid data"', () => 
        expect(() => createClosure(admin)()).toThrow(Errors.INVALID_DATA)
        expect(() => createClosure(admin)(data1)).toThrow(Errors.INVALID_DATA)
        expect(() => createClosure(admin)(data2)).toThrow(Errors.INVALID_DATA)
        expect(() => createClosure(admin)(data3)).toThrow(Errors.INVALID_DATA)
        expect(() => createClosure(admin)(data4)).toThrow(Errors.INVALID_DATA)
      )
    )
    describe('when data is valid', () => 
      const data = 
        firstName: 'Alice',
        lastName: 'Alley',
        foo: 'bar',
        baz: 
          boo: 'bii',
          buu: 'byy',
        ,
      
      describe('when createUser rejects', () => 
        const e = new Error('Error happened!')
        const createUser = jest.fn().mockRejectedValue(e)
        const admin = 
          auth: () => (
            createUser,
          ),
        
        const create = createClosure(admin)
        it('should call createUser once', async () => 
          try 
            await createUser(data)
           catch (error) 
          expect(createUser).toBeCalledTimes(1)
          expect(createUser).toBeCalledWith( ...data )
        )
        it('should reject', async () => 
          await expect(create(data)).rejects.toEqual(e)
        )
      )
      describe('when save resolves', () => 
        const expected = 
          baz:  boo: 'bii', buu: 'byy' ,
          displayName: 'Alice Alley',
          lastName: 'Alley',
        
        const displayName = `$data.firstName $data.lastName`
        const createUser = jest.fn().mockResolvedValue(expected)
        const admin = 
          auth: () => (
            createUser,
          ),
        
        const create = createClosure(admin)
        it('should call save once', async () => 
          try 
            await create(data)
           catch (error) 
          expect(createUser).toBeCalledTimes(1)
          expect(createUser).toBeCalledWith( ...data, displayName )
        )
        it('should resolve', async () => 
          const result = await create(data)
          expect(result).toMatchObject(expected)
        )
      )
    )
  )
)

【讨论】:

非常感谢。我将使用它作为参考,看看我是否可以运行我的测试。如果我设法解决测试,我将关闭问题。

以上是关于用 Jest 模拟 Firebase 功能(单元测试)的主要内容,如果未能解决你的问题,请参考以下文章

用 Jest 模拟基于承诺的请求

用 Jest 反应单元测试 - 模拟 localStorage 的问题

用 Jest 模拟 jsonwebtoken 模块

在 Jest 中模拟 Firebase 管理员时出错:“TypeError:admin.firestore 不是函数”

Jest单元测试

Jest 单元测试入门