Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.
Trang này được dịch bởi Cloud Translation API.
Switch to English

Kiểm tra đơn vị các chức năng đám mây

Trang này mô tả các phương pháp và công cụ tốt nhất để viết các bài kiểm tra đơn vị cho các chức năng của bạn, chẳng hạn như các bài kiểm tra sẽ là một phần của hệ thống Tích hợp Liên tục (CI). Để giúp việc kiểm tra dễ dàng hơn, Firebase cung cấp SDK kiểm tra Firebase cho Chức năng đám mây. Nó được phân phối trên npm dưới dạng firebase-functions-test và là SDK thử nghiệm đồng hành với firebase-functions . SDK kiểm tra Firebase cho các chức năng đám mây:

  • Chăm sóc thiết lập và gỡ bỏ thích hợp cho các thử nghiệm của bạn, chẳng hạn như cài đặt và hủy thiết lập các biến môi trường cần thiết cho firebase-functions .
  • Tạo dữ liệu mẫu và ngữ cảnh sự kiện để bạn chỉ phải chỉ định các trường có liên quan đến thử nghiệm của mình.

Thiết lập thử nghiệm

Cài đặt cả firebase-functions-testMocha , một khung thử nghiệm, bằng cách chạy các lệnh sau trong thư mục chức năng của bạn:

npm install --save-dev firebase-functions-test
npm install --save-dev mocha

Tiếp theo, tạo một thư mục test bên trong thư mục chức năng, tạo một tệp mới bên trong nó cho mã thử nghiệm của bạn và đặt tên nó như index.test.js .

Cuối cùng, sửa đổi functions/package.json để thêm những thứ sau:

"scripts": {
  "test": "mocha --reporter spec"
}

Khi bạn đã viết các bài kiểm tra, bạn có thể chạy chúng bằng cách chạy npm test bên trong thư mục chức năng của bạn.

Khởi tạo SDK kiểm tra Firebase cho các chức năng đám mây

Có hai cách để sử dụng firebase-functions-test :

  1. Chế độ trực tuyến (được khuyến nghị): Viết các bài kiểm tra tương tác với một dự án Firebase dành riêng để kiểm tra để cơ sở dữ liệu ghi, người dùng tạo, v.v. sẽ thực sự xảy ra và mã kiểm tra của bạn có thể kiểm tra kết quả. Điều này cũng có nghĩa là các SDK khác của Google được sử dụng trong các chức năng của bạn cũng sẽ hoạt động.
  2. Chế độ ngoại tuyến: Viết các bài kiểm tra đơn vị ẩn và ngoại tuyến mà không có tác dụng phụ. Điều này có nghĩa là bất kỳ lệnh gọi phương thức nào tương tác với sản phẩm Firebase (ví dụ: ghi vào cơ sở dữ liệu hoặc tạo người dùng) cần phải được xác định. Sử dụng chế độ ngoại tuyến thường không được khuyến khích nếu bạn có chức năng Cloud Firestore hoặc Cơ sở dữ liệu thời gian thực, vì nó làm tăng đáng kể độ phức tạp của mã thử nghiệm của bạn.

Khởi tạo SDK ở chế độ trực tuyến (được khuyến nghị)

Nếu bạn muốn viết các bài kiểm tra tương tác với một dự án thử nghiệm, bạn cần cung cấp các giá trị cấu hình dự án cần thiết để khởi chạy ứng dụng thông qua firebase-admin và đường dẫn đến tệp khóa tài khoản dịch vụ.

Để nhận các giá trị cấu hình của dự án Firebase của bạn:

  1. Mở cài đặt dự án của bạn trong Bảng điều khiển Firebase .
  2. Trong Ứng dụng của bạn, hãy chọn ứng dụng mong muốn.
  3. Trong ngăn bên phải, chọn tùy chọn tải xuống tệp cấu hình cho ứng dụng iOS và Android.

    Đối với ứng dụng web, chọn Cấu hình để hiển thị các giá trị cấu hình.

Để tạo một tệp khóa:

  1. Mở ngăn Tài khoản dịch vụ của Google Cloud Console.
  2. Chọn tài khoản dịch vụ mặc định của App Engine và sử dụng menu tùy chọn ở bên phải để chọn Tạo khóa .
  3. Khi được nhắc, hãy chọn JSON cho loại khóa và nhấp vào Tạo .

Sau khi lưu tệp khóa, hãy khởi chạy SDK:

// At the top of test/index.test.js
const test = require('firebase-functions-test')({
  databaseURL: 'https://my-project.firebaseio.com',
  storageBucket: 'my-project.appspot.com',
  projectId: 'my-project',
}, 'path/to/serviceAccountKey.json');

Khởi tạo SDK ở chế độ ngoại tuyến

Nếu bạn muốn viết các thử nghiệm hoàn toàn ngoại tuyến, bạn có thể khởi tạo SDK mà không có bất kỳ tham số nào:

// At the top of test/index.test.js
const test = require('firebase-functions-test')();

Chế nhạo các giá trị cấu hình

Nếu bạn sử dụng functions.config() trong mã chức năng của mình, bạn có thể giả lập các giá trị cấu hình. Ví dụ: nếu functions/index.js chứa mã sau:

const functions = require('firebase-functions');
const key = functions.config().stripe.key;

Sau đó, bạn có thể giả lập giá trị bên trong tệp thử nghiệm của mình như sau:

// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});

Nhập các chức năng của bạn

Để nhập các chức năng của bạn, hãy sử dụng require để nhập tệp chức năng chính của bạn dưới dạng mô-đun. Đảm bảo chỉ thực hiện việc này sau khi khởi tạo firebase-functions-test và các giá trị cấu hình mocking.

// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code

Nếu bạn khởi tạo firebase-functions-testchế độ ngoại tuyến và bạn có admin.initializeApp() trong mã chức năng của mình, thì bạn cần khai báo trước khi nhập các chức năng của mình:

// If index.js calls admin.initializeApp at the top of the file,
// we need to stub it out before requiring index.js. This is because the
// functions will be executed as a part of the require process.
// Here we stub admin.initializeApp to be a dummy function that doesn't do anything.
adminInitStub = sinon.stub(admin, 'initializeApp');
// Now we can require index.js and save the exports inside a namespace called myFunctions.
myFunctions = require('../index');

Kiểm tra các chức năng nền (không phải HTTP)

Quá trình kiểm tra các chức năng không phải HTTP bao gồm các bước sau:

  1. Gói chức năng bạn muốn kiểm tra bằng phương thức test.wrap
  2. Xây dựng dữ liệu thử nghiệm
  3. Gọi hàm được bao bọc với dữ liệu thử nghiệm bạn đã tạo và bất kỳ trường ngữ cảnh sự kiện nào bạn muốn chỉ định.
  4. Đưa ra khẳng định về hành vi.

Trước tiên, hãy gói chức năng bạn muốn kiểm tra. Giả sử bạn có một hàm trong functions/index.js được gọi là makeUppercase , mà bạn muốn kiểm tra. Viết nội dung sau trong functions/test/index.test.js

// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);

wrapped là một hàm gọi makeUppercase khi nó được gọi. wrapped có 2 tham số:

  1. data (bắt buộc): dữ liệu cần gửi đến makeUppercase . Điều này trực tiếp tương ứng với tham số đầu tiên được gửi đến trình xử lý hàm mà bạn đã viết. firebase-functions-test cung cấp các phương pháp để xây dựng dữ liệu tùy chỉnh hoặc dữ liệu ví dụ.
  2. eventContextOptions (tùy chọn): các trường của ngữ cảnh sự kiện mà bạn muốn chỉ định. Bối cảnh sự kiện là tham số thứ hai được gửi đến trình xử lý hàm mà bạn đã viết. Nếu bạn không bao gồm tham số eventContextOptions khi gọi wrapped , ngữ cảnh sự kiện vẫn được tạo với các trường hợp lý. Bạn có thể ghi đè một số trường đã tạo bằng cách chỉ định chúng ở đây. Lưu ý rằng bạn chỉ phải bao gồm các trường mà bạn muốn ghi đè. Bất kỳ trường nào bạn không ghi đè sẽ được tạo.
const data = … // See next section for constructing test data

// Invoke the wrapped function without specifying the event context.
wrapped(data);

// Invoke the function, and specify params
wrapped(data, {
  params: {
    pushId: '234234'
  }
});

// Invoke the function, and specify auth and auth Type (for real time database functions only)
wrapped(data, {
  auth: {
    uid: 'jckS2Q0'
  },
  authType: 'USER'
});

// Invoke the function, and specify all the fields that can be specified
wrapped(data, {
  eventId: 'abc',
  timestamp: '2018-03-23T17:27:17.099Z',
  params: {
    pushId: '234234'
  },
  auth: {
    uid: 'jckS2Q0' // only for real time database functions
  },
  authType: 'USER' // only for real time database functions
});

Xây dựng dữ liệu thử nghiệm

Tham số đầu tiên của một hàm được bao bọc là dữ liệu kiểm tra để gọi hàm cơ bản với. Có một số cách để xây dựng dữ liệu thử nghiệm.

Sử dụng dữ liệu tùy chỉnh

firebase-functions-test có một số chức năng để xây dựng dữ liệu cần thiết để kiểm tra các chức năng của bạn. Ví dụ: sử dụng test.firestore.makeDocumentSnapshot để tạo một Firestore DocumentSnapshot . Đối số đầu tiên là dữ liệu và đối số thứ hai là đường dẫn tham chiếu đầy đủ và có đối số thứ ba tùy chọn cho các thuộc tính khác của ảnh chụp nhanh mà bạn có thể chỉ định.

// Make snapshot
const snap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Call wrapped function with the snapshot
const wrapped = test.wrap(myFunctions.myFirestoreDeleteFunction);
wrapped(snap);

Nếu bạn đang thử nghiệm chức năng onUpdate hoặc onWrite , bạn sẽ cần tạo hai ảnh chụp nhanh: một cho trạng thái trước và một cho trạng thái sau. Sau đó, bạn có thể sử dụng phương thức makeChange để tạo một đối tượng Change với các ảnh chụp nhanh này.

// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot({foo: 'faz'}, 'document/path');
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(myFunctions.myFirestoreUpdateFunction);
wrapped(change);

Xem tham chiếu API để biết các hàm tương tự cho tất cả các kiểu dữ liệu khác.

Sử dụng dữ liệu mẫu

Nếu bạn không cần tùy chỉnh dữ liệu được sử dụng trong các thử nghiệm của mình, thì firebase-functions-test cung cấp các phương pháp tạo dữ liệu mẫu cho từng loại chức năng.

// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();

Xem tham chiếu API để biết các phương pháp lấy dữ liệu mẫu cho mọi loại hàm.

Sử dụng dữ liệu sơ bộ (cho chế độ ngoại tuyến)

Nếu bạn khởi tạo SDK ở chế độ ngoại tuyến và đang thử nghiệm chức năng Cloud Firestore hoặc Cơ sở dữ liệu thời gian thực, bạn nên sử dụng một đối tượng thuần túy có sơ khai thay vì tạo một DocumentSnapshot hoặc DataSnapshot thực tế.

Giả sử bạn đang viết một bài kiểm tra đơn vị cho chức năng sau:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

Bên trong hàm, snap được sử dụng hai lần:

  • snap.val()
  • snap.ref.parent.child('uppercase').set(uppercase)

Trong mã thử nghiệm, hãy tạo một đối tượng đơn giản nơi cả hai đường dẫn mã này sẽ hoạt động và sử dụng Sinon để khai thác các phương thức.

// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);

Khẳng định

Sau khi khởi tạo SDK, gói các chức năng và xây dựng dữ liệu, bạn có thể gọi các chức năng được gói với dữ liệu đã xây dựng và đưa ra xác nhận về hành vi. Bạn có thể sử dụng một thư viện như Chai để đưa ra những khẳng định này.

Đưa ra khẳng định trong chế độ trực tuyến

Nếu bạn đã khởi chạy SDK kiểm tra Firebase cho Chức năng đám mây ở chế độ trực tuyến , bạn có thể khẳng định rằng các hành động mong muốn (chẳng hạn như ghi cơ sở dữ liệu) đã diễn ra bằng cách sử dụng SDK firebase-admin .

Ví dụ dưới đây khẳng định rằng 'INPUT' đã được ghi vào cơ sở dữ liệu của dự án thử nghiệm.

// Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'.
const snap = test.database.makeDataSnapshot('input', 'messages/11111/original');

// Wrap the makeUppercase function
const wrapped = test.wrap(myFunctions.makeUppercase);
// Call the wrapped function with the snapshot you constructed.
return wrapped(snap).then(() => {
  // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is
  // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add
  // `admin.initializeApp()` before this line.
  return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => {
    // Assert that the value is the uppercased version of our input.
    assert.equal(createdSnap.val(), 'INPUT');
  });
});

Đưa ra xác nhận ở chế độ ngoại tuyến

Bạn có thể xác nhận về giá trị trả về mong đợi của hàm:

const childParam = 'uppercase';
const setParam = 'INPUT';
// Stubs are objects that fake and/or record function calls.
// These are excellent for verifying that functions have been called and to validate the
// parameters passed to those functions.
const childStub = sinon.stub();
const setStub = sinon.stub();
// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);
// Wrap the makeUppercase function.
const wrapped = test.wrap(myFunctions.makeUppercase);
// Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was
// called with the parameters we expect, we assert that it indeed returned true.
return assert.equal(wrapped(snap), true);

Bạn cũng có thể sử dụng gián điệp Sinon để khẳng định rằng một số phương thức nhất định đã được gọi và với các tham số bạn mong đợi.

Kiểm tra các chức năng HTTP

Để kiểm tra các chức năng HTTP onCall, hãy sử dụng phương pháp tương tự như kiểm tra các chức năng nền .

Nếu bạn đang thử nghiệm các chức năng onRequest của HTTP, bạn nên sử dụng firebase-functions-test nếu:

  • Bạn sử dụng functions.config()
  • Hàm của bạn tương tác với dự án Firebase hoặc các API khác của Google và bạn muốn sử dụng một dự án Firebase thực và thông tin đăng nhập của nó cho các thử nghiệm của mình.

Một hàm onRequest HTTP nhận hai tham số: một đối tượng yêu cầu và một đối tượng phản hồi. Đây là cách bạn có thể kiểm tra hàm ví dụ addMessage() :

  • Ghi đè hàm chuyển hướng trong đối tượng phản hồi, vì sendMessage() gọi nó.
  • Trong hàm chuyển hướng, hãy sử dụng chai.assert để giúp đưa ra xác nhận về các tham số mà hàm chuyển hướng sẽ được gọi với:
// A fake request object, with req.query.text set to 'input'
const req = { query: {text: 'input'} };
// A fake response object, with a stubbed redirect function which asserts that it is called
// with parameters 303, 'new_ref'.
const res = {
  redirect: (code, url) => {
    assert.equal(code, 303);
    assert.equal(url, 'new_ref');
    done();
  }
};

// Invoke addMessage with our fake request and response objects. This will cause the
// assertions in the response object to be evaluated.
myFunctions.addMessage(req, res);

Kiểm tra dọn dẹp

Vào cuối mã kiểm tra của bạn, hãy gọi hàm dọn dẹp. Điều này sẽ hủy đặt các biến môi trường mà SDK đã đặt khi khởi chạy và xóa các ứng dụng Firebase có thể đã được tạo nếu bạn sử dụng SDK để tạo cơ sở dữ liệu thời gian thực DataSnapshot hoặc Firestore DocumentSnapshot .

test.cleanup();

Xem lại các ví dụ hoàn chỉnh và tìm hiểu thêm

Bạn có thể xem lại các ví dụ đầy đủ trên kho lưu trữ Firebase GitHub.

Để tìm hiểu thêm, hãy tham khảo tham chiếu API cho firebase-functions-test .