每次我使用Firebase时,都会遇到如何测试Firebase的数据库和认证的问题。由于我使用Jest作为我的默认测试环境,我想我需要的一切都已经在Jest中出现了。在本教程中,你将学习如何模拟Firebase的功能。我们将使用Firebase Admin SDK来设置Firebase,然而,使用Firebase Real-Time Database、Firebase Firestore和Firebase Authentication的传统客户端Firebase也同样适用。
import * as firebaseAdmin from 'firebase-admin';
import firebaseServiceAccountKey from './firebaseServiceAccountKey.json';
if (!firebaseAdmin.apps.length) {
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(
firebaseServiceAccountKey
),
databaseURL: 'https://my-firebase-application.firebaseio.com',
});
}
export default firebaseAdmin;
在设置好Firebase之后,我们有了第一个数据库函数,它在Firebase的数据库中创建了一条记录:
import firebaseAdmin from './firebase';
export const createCourse = async ({
uid,
courseId,
bundleId,
amount,
paymentType,
}) => {
await firebaseAdmin
.database()
.ref(`users/${uid}/courses`)
.push()
.set({
courseId: courseId,
packageId: bundleId,
invoice: {
createdAt: firebaseAdmin.database.ServerValue.TIMESTAMP,
amount,
licensesCount: 1,
currency: 'USD',
paymentType,
},
});
return true;
}
在我们的测试文件中,用Jest做的测试可以类似于这个测试Firebase函数:
import { createCourse } from './';
import firebaseAdmin from './firebase';
describe('createFreeCourse', () => {
it('creates a course', async () => {
const set = firebaseAdmin
.database()
.ref()
.push().set;
const result = createCourse(
'1',
'THE_ROAD_TO_GRAPHQL',
'STUDENT',
0,
'FREE'
);
await expect(result).resolves.toEqual(true);
expect(set).toHaveBeenCalledTimes(1);
expect(set).toHaveBeenCalledWith({
courseId: 'THE_ROAD_TO_GRAPHQL',
packageId: 'STUDENT',
invoice: {
createdAt: 'TIMESTAMP',
amount: 0,
licensesCount: 1,
currency: 'USD',
paymentType: 'FREE',
},
});
});
});
在这个测试运行之前,我们需要在测试文件中模拟Firebase来覆盖有问题的行(突出显示)。我们不把Firebase当作库来模拟,而是模拟发生在另一个文件中的设置,我之前已经展示过。
import { createCourse } from './';
import firebaseAdmin from './firebase';
jest.mock('./firebase', () => {
const set = jest.fn();
return {
database: jest.fn(() => ({
ref: jest.fn(() => ({
push: jest.fn(() => ({
set,
})),
})),
})),
};
});
describe('createFreeCourse', () => {
...
});
现在可以调用Jest的toHaveBeenCalledTimes() 和toHaveBeenCalledWith() 在被模拟的函数上。然而,我们仍然没有正确地模拟Firebase的时间戳。在我们的源代码中,让我们使用明确的Firebase导入,而不是我们的Firebase设置的时间戳。
import * as firebaseAdminVanilla from 'firebase-admin';
import firebaseAdmin from './firebase';
export const createCourse = async ({
uid,
courseId,
bundleId,
amount,
paymentType,
}) =>
await firebaseAdmin
.database()
.ref(`users/${uid}/courses`)
.push()
.set({
courseId: courseId,
packageId: bundleId,
invoice: {
createdAt: firebaseAdminVanilla.database.ServerValue.TIMESTAMP,
amount,
licensesCount: 1,
currency: 'USD',
paymentType,
},
});
现在,我们可以在测试中用Jest来模拟Firebase常量的Firebase导入:
import { createCourse } from './';
import firebaseAdmin from './firebase';
jest.mock('firebase-admin', () => {
return {
database: {
ServerValue: {
TIMESTAMP: 'TIMESTAMP',
},
},
};
});
jest.mock('./firebase', () => {
...
});
describe('createFreeCourse', () => {
...
});
Firebase常量现在在我们的测试断言中应该是正常的。你也可以考虑把最后一个Firebase模拟移到你的jest.setup.js 文件中,因为其他单元和集成测试也可能需要它。毕竟,你现在应该已经掌握了测试Firebase应用程序的一切。