এই পৃষ্ঠাটি আপনার ফাংশনগুলির জন্য ইউনিট টেস্ট লেখার সেরা পদ্ধতি এবং টুলগুলি বর্ণনা করে, যেমন যে টেস্টগুলি একটি কন্টিনিউয়াস ইন্টিগ্রেশন (CI) সিস্টেমের অংশ হবে। টেস্টিং সহজ করার জন্য, Firebase Cloud Functions জন্য Firebase Test SDK প্রদান করে। এটি npm-এ firebase-functions-test নামে বিতরণ করা হয় এবং এটি firebase-functions এর একটি সহযোগী টেস্ট SDK। Cloud Functions জন্য Firebase Test SDK :
- আপনার টেস্টগুলোর জন্য যথাযথ সেটআপ এবং টিয়ারডাউনের ব্যবস্থা করে, যেমন
firebase-functionsএর জন্য প্রয়োজনীয় এনভায়রনমেন্ট ভেরিয়েবল সেট করা ও আনসেট করা। - নমুনা ডেটা এবং ইভেন্ট কনটেক্সট তৈরি করে, ফলে আপনাকে শুধু আপনার পরীক্ষার জন্য প্রাসঙ্গিক ফিল্ডগুলোই নির্দিষ্ট করতে হয়।
পরীক্ষার সেটআপ
আপনার functions ফোল্ডারে নিম্নলিখিত কমান্ডগুলো চালিয়ে firebase-functions-test এবং Mocha (একটি টেস্টিং ফ্রেমওয়ার্ক) উভয়ই ইনস্টল করুন:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
এরপর functions ফোল্ডারের ভিতরে একটি test ফোল্ডার তৈরি করুন, তার ভিতরে আপনার টেস্ট কোডের জন্য একটি নতুন ফাইল তৈরি করুন এবং এটির নাম index.test.js মতো কিছু একটা দিন।
অবশেষে, functions/package.json পরিবর্তন করে নিম্নলিখিত বিষয়গুলো যোগ করুন:
"scripts": {
"test": "mocha --reporter spec"
}
টেস্টগুলো লেখা হয়ে গেলে, আপনার functions ডিরেক্টরির ভেতরে npm test চালিয়ে সেগুলো রান করতে পারেন।
Cloud Functions জন্য Firebase Test SDK শুরু করা হচ্ছে
firebase-functions-test ব্যবহার করার দুটি উপায় আছে:
- অনলাইন মোড (প্রস্তাবিত): এমন টেস্ট লিখুন যা পরীক্ষার জন্য বিশেষভাবে তৈরি একটি ফায়ারবেস প্রজেক্টের সাথে কাজ করে, যাতে ডাটাবেসে লেখা, ব্যবহারকারী তৈরি করা ইত্যাদি কাজগুলো বাস্তবে ঘটে এবং আপনার টেস্ট কোড তার ফলাফল পরীক্ষা করতে পারে। এর মানে হলো, আপনার ফাংশনগুলোতে ব্যবহৃত অন্যান্য গুগল এসডিকে-গুলোও কাজ করবে।
- অফলাইন মোড: কোনো সাইড এফেক্ট ছাড়া বিচ্ছিন্ন এবং অফলাইন ইউনিট টেস্ট লিখুন। এর মানে হলো, ফায়ারবেস প্রোডাক্টের সাথে ইন্টারঅ্যাক্ট করে এমন যেকোনো মেথড কল (যেমন ডেটাবেসে লেখা বা ইউজার তৈরি করা) স্টাব করতে হবে। আপনার যদি Cloud Firestore বা Realtime Database ফাংশন থাকে, তবে সাধারণত অফলাইন মোড ব্যবহার করার পরামর্শ দেওয়া হয় না, কারণ এটি আপনার টেস্ট কোডের জটিলতা অনেক বাড়িয়ে দেয়।
অনলাইন মোডে SDK চালু করুন (প্রস্তাবিত)
আপনি যদি এমন টেস্ট লিখতে চান যা কোনো টেস্ট প্রজেক্টের সাথে ইন্টারঅ্যাক্ট করে, তাহলে আপনাকে firebase-admin এর মাধ্যমে অ্যাপটি ইনিশিয়ালাইজ করার জন্য প্রয়োজনীয় প্রজেক্ট কনফিগ ভ্যালু এবং একটি সার্ভিস অ্যাকাউন্ট কী ফাইলের পাথ সরবরাহ করতে হবে।
আপনার Firebase প্রোজেক্টের কনফিগ ভ্যালুগুলো পেতে:
- Firebase কনসোলে আপনার প্রোজেক্ট সেটিংস খুলুন।
- আপনার অ্যাপস থেকে কাঙ্ক্ষিত অ্যাপটি নির্বাচন করুন।
ডান দিকের প্যানেলে, অ্যাপল এবং অ্যান্ড্রয়েড অ্যাপের জন্য কনফিগারেশন ফাইল ডাউনলোড করার অপশনটি নির্বাচন করুন।
ওয়েব অ্যাপের ক্ষেত্রে, কনফিগারেশন মানগুলো দেখতে Config নির্বাচন করুন।
একটি কী ফাইল তৈরি করতে:
- Google Cloud কনসোলের সার্ভিস অ্যাকাউন্টস প্যানেটি খুলুন।
- App Engine ডিফল্ট সার্ভিস অ্যাকাউন্টটি নির্বাচন করুন এবং ডানদিকের অপশন মেনু থেকে ‘Create key’ নির্বাচন করুন।
- নির্দেশিত হলে, কী টাইপ হিসেবে JSON নির্বাচন করুন এবং Create-এ ক্লিক করুন।
কী ফাইলটি সংরক্ষণ করার পর, SDK চালু করুন:
// At the top of test/index.test.js
// Make sure to use values from your actual Firebase configuration
const test = require('firebase-functions-test')({
databaseURL: 'https://PROJECT_ID.firebaseio.com',
storageBucket: 'PROJECT_ID.firebasestorage.app ',
projectId: 'PROJECT_ID',
}, 'path/to/serviceAccountKey.json');
অফলাইন মোডে SDK শুরু করুন
আপনি যদি সম্পূর্ণ অফলাইন টেস্ট লিখতে চান, তাহলে কোনো প্যারামিটার ছাড়াই SDK-টি ইনিশিয়ালাইজ করতে পারেন:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
কনফিগারেশন মান নকল করা
আপনার functions কোডে functions.config() ব্যবহার করলে, আপনি config-এর মানগুলো মক (mock) করতে পারবেন। উদাহরণস্বরূপ, যদি functions/index.js নিম্নলিখিত কোডটি থাকে:
const functions = require('firebase-functions/v1');
const key = functions.config().stripe.key;
তারপর আপনি আপনার টেস্ট ফাইলের ভিতরে মানটিকে এইভাবে মক করতে পারেন:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
আপনার ফাংশনগুলি আমদানি করা
আপনার ফাংশনগুলো ইম্পোর্ট করতে, require ব্যবহার করে আপনার প্রধান ফাংশন ফাইলটিকে একটি মডিউল হিসেবে ইম্পোর্ট করুন। অবশ্যই খেয়াল রাখবেন যেন firebase-functions-test ইনিশিয়ালাইজ করার এবং কনফিগের ভ্যালুগুলো মক করার পরেই কেবল এটি করা হয়।
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
আপনি যদি firebase-functions-test অফলাইন মোডে ইনিশিয়ালাইজ করে থাকেন এবং আপনার functions কোডে admin.initializeApp() থাকে, তাহলে আপনার functions ইম্পোর্ট করার আগে এটিকে স্টাব করতে হবে:
// 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');
ব্যাকগ্রাউন্ড (নন-HTTP) ফাংশন পরীক্ষা করা হচ্ছে
নন-HTTP ফাংশনগুলো পরীক্ষা করার প্রক্রিয়ায় নিম্নলিখিত ধাপগুলো অন্তর্ভুক্ত রয়েছে:
- যে ফাংশনটি আপনি পরীক্ষা করতে চান, সেটিকে
test.wrapমেথড দিয়ে মুড়ে দিন। - পরীক্ষার ডেটা তৈরি করুন
- আপনার তৈরি করা টেস্ট ডেটা এবং আপনার নির্দিষ্ট করতে চাওয়া যেকোনো ইভেন্ট কনটেক্সট ফিল্ড সহ র্যাপ করা ফাংশনটি কল করুন।
- আচরণ সম্পর্কে দাবি করুন।
প্রথমে আপনি যে ফাংশনটি পরীক্ষা করতে চান, সেটিকে একটি র্যাপারের মধ্যে আনুন। ধরা যাক, আপনার functions/index.js ফাইলে makeUppercase নামে একটি ফাংশন আছে, যেটি আপনি পরীক্ষা করতে চান। functions/test/index.test.js ফাইলে নিম্নলিখিত কোডটি লিখুন।
// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);
wrapped হলো এমন একটি ফাংশন যা কল করা হলে makeUppercase চালু করে। wrapped দুটি প্যারামিটার গ্রহণ করে:
- ডেটা (প্রয়োজনীয়):
makeUppercaseএ পাঠানোর জন্য ডেটা। এটি আপনার লেখা ফাংশন হ্যান্ডলারে পাঠানো প্রথম প্যারামিটারের সাথে সরাসরি সম্পর্কিত।firebase-functions-testনিজস্ব ডেটা বা উদাহরণ ডেটা তৈরি করার জন্য মেথড সরবরাহ করে। - eventContextOptions (ঐচ্ছিক): ইভেন্ট কনটেক্সটের যে ফিল্ডগুলো আপনি নির্দিষ্ট করতে চান। ইভেন্ট কনটেক্সট হলো আপনার লেখা ফাংশন হ্যান্ডলারে পাঠানো দ্বিতীয় প্যারামিটার। আপনি যদি
wrappedকল করার সময়eventContextOptionsপ্যারামিটারটি অন্তর্ভুক্ত না করেন, তবুও প্রাসঙ্গিক ফিল্ডসহ একটি ইভেন্ট কনটেক্সট তৈরি হয়ে যায়। আপনি এখানে নির্দিষ্ট করে তৈরি হওয়া কিছু ফিল্ড ওভাররাইড করতে পারেন। মনে রাখবেন, আপনাকে শুধুমাত্র সেই ফিল্ডগুলোই অন্তর্ভুক্ত করতে হবে যেগুলো আপনি ওভাররাইড করতে চান। যে ফিল্ডগুলো আপনি ওভাররাইড করেননি, সেগুলো তৈরি হয়ে যাবে।
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
});
পরীক্ষার ডেটা তৈরি করা
একটি র্যাপড ফাংশনের প্রথম প্যারামিটারটি হলো সেই টেস্ট ডেটা, যা দিয়ে অন্তর্নিহিত ফাংশনটিকে কল করা হয়। টেস্ট ডেটা তৈরি করার বিভিন্ন উপায় রয়েছে।
কাস্টম ডেটা ব্যবহার করে
firebase-functions-test আপনার ফাংশনগুলো পরীক্ষা করার জন্য প্রয়োজনীয় ডেটা তৈরি করার বেশ কিছু ফাংশন রয়েছে। উদাহরণস্বরূপ, একটি Firestore DocumentSnapshot তৈরি করতে test.firestore.makeDocumentSnapshot ব্যবহার করুন। এর প্রথম আর্গুমেন্টটি হলো ডেটা, দ্বিতীয় আর্গুমেন্টটি হলো সম্পূর্ণ রেফারেন্স পাথ, এবং স্ন্যাপশটের অন্যান্য প্রোপার্টিগুলোর জন্য একটি ঐচ্ছিক তৃতীয় আর্গুমেন্টও রয়েছে যা আপনি নির্দিষ্ট করতে পারেন।
// 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);
আপনি যদি কোনো onUpdate বা onWrite ফাংশন পরীক্ষা করেন, তাহলে আপনাকে দুটি স্ন্যাপশট তৈরি করতে হবে: একটি আগের অবস্থার জন্য এবং অন্যটি পরের অবস্থার জন্য। তারপর, আপনি এই স্ন্যাপশটগুলো ব্যবহার করে makeChange মেথডের মাধ্যমে একটি Change অবজেক্ট তৈরি করতে পারবেন।
// 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);
অন্যান্য সকল ডেটা টাইপের জন্য অনুরূপ ফাংশনগুলোর জন্য এপিআই রেফারেন্স দেখুন।
উদাহরণ ডেটা ব্যবহার করে
আপনার টেস্টে ব্যবহৃত ডেটা কাস্টমাইজ করার প্রয়োজন না হলে, firebase-functions-test প্রতিটি ফাংশন টাইপের জন্য উদাহরণ ডেটা তৈরি করার পদ্ধতি প্রদান করে।
// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();
প্রতিটি ফাংশন টাইপের উদাহরণ ডেটা পাওয়ার পদ্ধতিগুলোর জন্য এপিআই রেফারেন্স দেখুন।
স্টাবড ডেটা ব্যবহার করা (অফলাইন মোডের জন্য)
আপনি যদি অফলাইন মোডে SDK চালু করে থাকেন এবং কোনো Cloud Firestore বা Realtime Database ফাংশন পরীক্ষা করেন, তাহলে প্রকৃত DocumentSnapshot বা DataSnapshot তৈরি করার পরিবর্তে স্টাবসহ একটি সাধারণ অবজেক্ট ব্যবহার করা উচিত।
ধরা যাক, আপনি নিম্নলিখিত ফাংশনটির জন্য একটি ইউনিট টেস্ট লিখছেন:
// 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(); functions.logger.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); });
ফাংশনের ভিতরে snap দুইবার ব্যবহার করা হয়েছে:
-
snap.val() -
snap.ref.parent.child('uppercase').set(uppercase)
টেস্ট কোডে, এমন একটি সাধারণ অবজেক্ট তৈরি করুন যেখানে এই উভয় কোড পাথই কাজ করবে, এবং মেথডগুলোকে স্টাব করতে Sinon ব্যবহার করুন।
// 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);
দাবি করা
এসডিকে ইনিশিয়ালাইজ করার পর, ফাংশনগুলোকে র্যাপ করে এবং ডেটা তৈরি করার পরে, আপনি তৈরি করা ডেটা দিয়ে র্যাপ করা ফাংশনগুলোকে কল করতে পারেন এবং তাদের আচরণ সম্পর্কে অ্যাসারশন করতে পারেন। এই অ্যাসারশনগুলো করার জন্য আপনি Chai-এর মতো কোনো লাইব্রেরি ব্যবহার করতে পারেন।
অনলাইন মোডে দাবি করা
আপনি যদি অনলাইন মোডে ক্লাউড ফাংশনের জন্য Firebase Test SDK for Cloud Functions ) চালু করে থাকেন, firebase-admin SDK ব্যবহার করে আপনি যাচাই করতে পারেন যে কাঙ্ক্ষিত কাজগুলো (যেমন ডেটাবেসে লেখা) সম্পন্ন হয়েছে কিনা।
নিচের উদাহরণটি নিশ্চিত করে যে টেস্ট প্রজেক্টের ডেটাবেসে 'INPUT' লেখা হয়েছে।
// 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'); }); });
অফলাইন মোডে দাবি করা
আপনি ফাংশনটির প্রত্যাশিত রিটার্ন মান সম্পর্কে অ্যাসারশন করতে পারেন:
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 wrapped(snap).then(makeUppercaseResult => { return assert.equal(makeUppercaseResult, true); });
নির্দিষ্ট কিছু মেথড আপনার প্রত্যাশিত প্যারামিটারসহ কল করা হয়েছে কিনা, তা নিশ্চিত করতেও আপনি সিনন স্পাই ব্যবহার করতে পারেন।
HTTP ফাংশন পরীক্ষা করা
HTTP onCall ফাংশনগুলো পরীক্ষা করার জন্য, ব্যাকগ্রাউন্ড ফাংশন পরীক্ষা করার পদ্ধতিটিই ব্যবহার করুন।
আপনি যদি HTTP onRequest ফাংশনগুলো পরীক্ষা করেন, তাহলে আপনার firebase-functions-test ব্যবহার করা উচিত যদি:
- আপনি
functions.config()ব্যবহার করেন - আপনার ফাংশনটি একটি Firebase প্রজেক্ট বা অন্যান্য Google API-এর সাথে কাজ করে, এবং আপনি আপনার পরীক্ষার জন্য একটি আসল Firebase প্রজেক্ট ও তার ক্রেডেনশিয়াল ব্যবহার করতে চান।
একটি HTTP onRequest ফাংশন দুটি প্যারামিটার গ্রহণ করে: একটি রিকোয়েস্ট অবজেক্ট এবং একটি রেসপন্স অবজেক্ট। নিচে দেওয়া হলো কীভাবে আপনি addMessage() উদাহরণ ফাংশনটি পরীক্ষা করতে পারেন:
- রেসপন্স অবজেক্টে রিডাইরেক্ট ফাংশনটি ওভাররাইড করুন, যেহেতু
sendMessage()ফাংশনটি এটিকে কল করে। - রিডাইরেক্ট ফাংশনের ভিতরে, ফাংশনটি কী কী প্যারামিটার দিয়ে কল করা উচিত সে সম্পর্কে অ্যাসারশন তৈরি করতে chai.assert ব্যবহার করুন:
// 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);
পরীক্ষার পরিচ্ছন্নতা
আপনার টেস্ট কোডের একেবারে শেষে, ক্লিনআপ ফাংশনটি কল করুন। এটি SDK ইনিশিয়ালাইজ করার সময় সেট করা এনভায়রনমেন্ট ভেরিয়েবলগুলোকে আনসেট করে এবং সেইসব Firebase অ্যাপ মুছে দেয়, যেগুলো হয়তো তৈরি হয়ে থাকতে পারে যদি আপনি SDK ব্যবহার করে একটি রিয়েল টাইম ডাটাবেস DataSnapshot বা Firestore DocumentSnapshot তৈরি করে থাকেন।
test.cleanup();
সম্পূর্ণ উদাহরণগুলো পর্যালোচনা করুন এবং আরও জানুন।
আপনি ফায়ারবেস গিটহাব রিপোজিটরিতে সম্পূর্ণ উদাহরণগুলো দেখে নিতে পারেন।
- অনলাইন মোডে Realtime Database এবং HTTP ফাংশন পরীক্ষা করা
- অফলাইন মোডে Realtime Database এবং HTTP ফাংশন পরীক্ষা করা
আরও জানতে, firebase-functions-test এর এপিআই রেফারেন্স দেখুন।