Bre*_*ill 4 javascript firebase jestjs google-cloud-firestore
我有一系列功能,每个功能执行各种Firestore交互。如何使用Jest模拟这些Firestore调用?我想避免使用图书馆。
当我使用jest.mock("firebase/app")
和jest.mock("firebase/firestore")
其他版本时,我将得到null的TypeErrors或表明我仍在引用实际导入而不是模拟的错误:Error: ... make sure you call initializeApp()
。
例如,我要测试的一个简单函数:
import firebase from "firebase/app";
import "firebase/firestore";
export const setDocData = (id, data) => {
const newDoc = {
created: firebase.firestore.FieldValue.serverTimestamp(),
...data
};
firebase
.firestore()
.doc("docs/" + id)
.set(newDoc);
};
Run Code Online (Sandbox Code Playgroud)
请注意,如何照常导入firebase,然后导入firestore的副作用。还要注意,如何首先将Firestore调用为函数,然后再引用为属性。我相信这是我麻烦的根源。
teo*_*one 11
关于这个问题的任何活动已经有一段时间了,但网上仍然没有太多材料,这是我的解决方案:
export default class FirestoreMock {
constructor () {
// mocked methods that return the class
this.mockCollection = jest.fn(() => this)
this.mockWhere = jest.fn(() => this)
this.mockOrderBy = jest.fn(() => this)
// methods that return promises
this.mockAdd = jest.fn(() => Promise.resolve(this._mockAddReturn))
this.mockGet = jest.fn(() => Promise.resolve(this._mockGetReturn))
// methods that accepts callbacks
this.mockOnSnaptshot = jest.fn((success, error) => success(this._mockOnSnaptshotSuccess))
// return values
this._mockAddReturn = null
this._mockGetReturn = null
this._mockOnSnaptshotSuccess = null
}
collection (c) {
return this.mockCollection(c)
}
where (...args) {
return this.mockWhere(...args)
}
orderBy (...args) {
return this.mockOrderBy(...args)
}
add (a) {
return this.mockAdd(a)
}
get () {
return this.mockGet()
}
onSnapshot (success, error) {
return this.mockOnSnaptshot(success, error)
}
set mockAddReturn (val) {
this._mockAddReturn = val
}
set mockGetReturn (val) {
this._mockGetReturn = val
}
set mockOnSnaptshotSuccess (val) {
this._mockOnSnaptshotSuccess = val
}
reset () {
// reset all the mocked returns
this._mockAddReturn = null
this._mockGetReturn = null
this._mockOnSnaptshotSuccess = null
// reset all the mocked functions
this.mockCollection.mockClear()
this.mockWhere.mockClear()
this.mockOrderBy.mockClear()
this.mockAdd.mockClear()
this.mockGet.mockClear()
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例用法:
import FirestoreMock from '../test_helpers/firestore.mock'
import firebase from 'firebase/app'
import 'firebase/firestore'
describe('The Agreement model', () => {
const firestoreMock = new FirestoreMock()
beforeEach(() => {
firebase.firestore = firestoreMock
firestoreMock.reset()
})
it('does something', (done) => {
firestoreMock.mockAddReturn = { id: 'test-id' }
firebase.firestore.collection('foobar')
.add({foo: 'bar'})
.then(res => {
expect(firestoreMock.mockCollection).toBeCalledWith('foobar')
expect(firestoreMock.mockAdd).toBeCalledWith({foo: 'bar'})
expect(res.id).toEqual('test-id')
done()
})
.catch(done)
})
})
Run Code Online (Sandbox Code Playgroud)
如果有任何兴趣,我可以打包FirestoreMock
实现,以便可以轻松共享
特奥
这是我找到的解决方案。网上没有太多有关此的信息,所以希望对您有所帮助。
诀窍是创建模拟函数的链接API,并将其设置在firebase对象上,而不是导入和模拟firestore。下面的示例使我可以测试上述示例功能,并且可以doc().get()
保证。
const docData = { data: "MOCK_DATA" };
const docResult = {
// simulate firestore get doc.data() function
data: () => docData
};
const get = jest.fn(() => Promise.resolve(docResult));
const set = jest.fn();
const doc = jest.fn(() => {
return {
set,
get
};
});
const firestore = () => {
return { doc };
};
firestore.FieldValue = {
serverTimestamp: () => {
return "MOCK_TIME";
}
};
export { firestore };
Run Code Online (Sandbox Code Playgroud)
我在所有测试执行之前运行的文件中声明它(请参阅文档),然后将其导入并在我的测试文件中使用,如下所示:
import firebase from "firebase/app";
import { firestore } from "../setupTests";
firebase.firestore = firestore;
describe("setDocData", () => {
const mockData = { fake: "data" };
beforeEach(() => {
jest.clearAllMocks();
setDocData("fakeDocID", mockData);
});
it("writes the correct doc", () => {
expect(firestore().doc).toHaveBeenCalledWith("docs/fakeDocID");
});
it("adds a timestamp, and writes it to the doc", () => {
expect(firestore().doc().set).toHaveBeenCalledWith({
created: "MOCK_TIME",
fake: "data"
});
});
});
Run Code Online (Sandbox Code Playgroud)
小智 7
如果嘲笑看起来很乏味,请不要。使用模拟器。
我相信这是在测试中处理读写的一个相对较新的选项,所以我发布了它。这是一个快速演练。
$ curl -sL firebase.tools | bash
Run Code Online (Sandbox Code Playgroud)
$ firebase init
Run Code Online (Sandbox Code Playgroud)
const db = firebase.initializeApp(config).firestore()
if (location.hostname === "localhost") {
db.settings({
host: "localhost:8080",
ssl: false
});
}
Run Code Online (Sandbox Code Playgroud)
$ firebase emulators:start
Run Code Online (Sandbox Code Playgroud)
describe('New city', () => {
it('should create a new city in firestore', async () => {
await db.collection('cities').doc('Seattle').set({ state: "WA" })
const city = await db.collection('cities').doc("Seattle").get()
expect(city.data()['population']).toEqual("WA")
})
})
Run Code Online (Sandbox Code Playgroud)
async function cleanFirestore() {
const Http = new XMLHttpRequest();
const url = "http://localhost:8080/emulator/v1/projects/<YOUR-PROJECT-ID>/databases/(default)/documents"
Http.open("DELETE", url);
Http.send();
return new Promise((resolve, reject) => {
setTimeout(reject, 2000)
Http.onreadystatechange = resolve
})
}
Run Code Online (Sandbox Code Playgroud)
对于来自 Google 的模拟器演练指南:https : //google.dev/pathways/firebase-emulators
文档:https : //firebase.google.com/docs/emulator-suite
小智 5
这是我嘲笑 firebase 的方式。
'use strict'
const collection = jest.fn(() => {
return {
doc: jest.fn(() => {
return {
collection: collection,
update: jest.fn(() => Promise.resolve(true)),
onSnapshot: jest.fn(() => Promise.resolve(true)),
get: jest.fn(() => Promise.resolve(true))
}
}),
where: jest.fn(() => {
return {
get: jest.fn(() => Promise.resolve(true)),
onSnapshot: jest.fn(() => Promise.resolve(true)),
}
})
}
});
const Firestore = () => {
return {
collection
}
}
Firestore.FieldValue = {
serverTimestamp: jest.fn()
}
export default class RNFirebase {
static initializeApp = jest.fn();
static auth = jest.fn(() => {
return {
createUserAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
sendPasswordResetEmail: jest.fn(() => Promise.resolve(true)),
signInAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
fetchSignInMethodsForEmail: jest.fn(() => Promise.resolve(true)),
signOut: jest.fn(() => Promise.resolve(true)),
onAuthStateChanged: jest.fn(),
currentUser: {
sendEmailVerification: jest.fn(() => Promise.resolve(true))
}
}
});
static firestore = Firestore;
static notifications = jest.fn(() => {
return {
onNotification: jest.fn(),
onNotificationDisplayed: jest.fn(),
onNotificationOpened: jest.fn()
}
});
static messaging = jest.fn(() => {
return {
hasPermission: jest.fn(() => Promise.resolve(true)),
subscribeToTopic: jest.fn(),
unsubscribeFromTopic: jest.fn(),
requestPermission: jest.fn(() => Promise.resolve(true)),
getToken: jest.fn(() => Promise.resolve('RN-Firebase-Token'))
}
});
static storage = jest.fn(() => {
return {
ref: jest.fn(() => {
return {
child: jest.fn(() => {
return {
put: jest.fn(() => Promise.resolve(true))
}
})
}
})
}
})
}
Run Code Online (Sandbox Code Playgroud)
我在组件上使用了依赖注入方法,这意味着我可以在没有所有样板的情况下模拟和测试方法。
例如,我有一个处理邀请的表单组件,如下所示:
import React, { useEffect } from 'react';
import { Formik } from 'formik';
import { validations } from '../../helpers';
import { checkIfTeamExists } from '../helpers';
const Invite = ({ send, userEmail, handleTeamCreation, auth, db, dbWhere }) => {
useEffect(() => {
checkIfTeamExists(send, dbWhere);
}, []);
return (
<Formik
initialValues={{ email: '' }}
onSubmit={values =>
handleTeamCreation(userEmail, values.email, db, auth, send)
}
validate={validations}
render={props => (
<form onSubmit={props.handleSubmit} data-testid="form">
<input
type="email"
placeholder="Please enter your email."
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
{props.errors.email && (
<p className="red" data-testid="error">
{props.errors.email}
</p>
)}
<button type="submit">Submit</button>
</form>
)}
/>
);
};
export default Invite;
Run Code Online (Sandbox Code Playgroud)
该checkIfTeamExists
方法依赖于 firebase 身份验证和handleTeamCreation
方法写入 firestore。
当我在其父级中引用该组件时,我像这样实例化了它:
<Invite
send={send}
userEmail={value.user.user.email}
handleTeamCreation={handleTeamCreation}
auth={auth.sendSignInLinkToEmail}
db={db.collection('games')}
dbWhere={db.collection('games')
.where('player1', '==', value.user.user.email)
.get}
/>
Run Code Online (Sandbox Code Playgroud)
然后,react-testing-library
在我的测试中,使用 , 我能够用一个简单的jest.fn()
.
test('Invite form fires the send function on Submit ', async () => {
const handleTeamCreation = jest.fn();
const send = jest.fn();
const userEmail = 'ex@mple.com';
const db = jest.fn();
const auth = jest.fn();
const dbWhere = jest.fn().mockResolvedValue([]);
const { getByPlaceholderText, getByTestId } = render(
<Invite
send={send}
userEmail={userEmail}
handleTeamCreation={handleTeamCreation}
auth={auth}
db={db}
dbWhere={dbWhere}
/>
);
const inputNode = getByPlaceholderText('Please enter your email.');
const email = 'me@gmail.com';
fireEvent.change(inputNode, { target: { value: email } });
const formNode = getByTestId('form');
fireEvent.submit(formNode);
await wait(() => {
expect(handleTeamCreation).toHaveBeenCalledWith(
userEmail,
email,
db,
auth,
send
);
expect(handleTeamCreation).toHaveBeenCalledTimes(1);
});
});
Run Code Online (Sandbox Code Playgroud)
并以相同的方式嘲笑 firestore where 查询。
test('Invite form must contain a valid email address', async () => {
const send = jest.fn();
const db = jest.fn();
const dbWhere = jest.fn().mockResolvedValue([]);
const { getByPlaceholderText, queryByTestId } = render(
<Invite send={send} db={db} dbWhere={dbWhere} />
);
expect(queryByTestId('error')).not.toBeInTheDocument();
const inputNode = getByPlaceholderText('Please enter your email.');
const email = 'x';
fireEvent.change(inputNode, { target: { value: email } });
await wait(() => {
expect(queryByTestId('error')).toHaveTextContent('Invalid email address');
});
});
Run Code Online (Sandbox Code Playgroud)
这很简单,但是很有效。它也很冗长,但我认为真实的用例比人为的示例更有帮助。我希望这可以帮助别人。
归档时间: |
|
查看次数: |
5082 次 |
最近记录: |